Merge tag 'dmaengine-7.2-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/vkoul/dmaengine

Pull dmaengine updates from Vinod Koul:
 "Core:
   - New devm_of_dma_controller_register() API
   - Refactor devm_dma_request_chan() API

  New Support:
   - Loongson Multi-Channel DMA controller support
   - Renesas RZ/{T2H,N2H} support
   - Dw CV1800B DMA support
   - Switchtec DMA engine driver

 U pdates:
   - Xilinx AXI dma binding conversion
   - Renesas CHCTRL register read updates
   - AMD MDB Endpoint and non-LL mode Support
   - AXI dma handling of SW and HW cyclic transfers termination
   - Intel ioatdma and idxd driver updates"

* tag 'dmaengine-7.2-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/vkoul/dmaengine: (62 commits)
  dt-bindings: dma: snps,dw-axi-dmac: Add fallback compatible for CV1800B
  MAINTAINERS: dmaengine/ti: Remove myself and add Vignesh as maintainer
  dmaengine: qcom: Unify user-visible "Qualcomm" name
  dt-bindings: dma: qcom,gpi: Document GPI DMA engine for Shikra SoC
  dmaengine: qcom: hidma: use sysfs_emit() in sysfs show callbacks
  dmaengine: dw-axi-dmac: fix PM for system sleep and channel alloc
  dmaengine: dw-axi-dmac: drop redundant DMAC enable in block start
  dmaengine: altera-msgdma: Use memcpy_toio for descriptor FIFO writes
  dt-bindings: dma: fsl-edma: add dma-channel-mask property description
  dmaengine: tegra: Fix burst size calculation
  dmaengine: iop32x-adma: Remove a leftover header file
  dmaengine: dma-axi-dmac: use DMA pool to manange DMA descriptor
  dmaengine: dma-axi-dmac: Drop struct clk from main struct
  dmaengine: dma-axi-dmac: Properly free struct axi_dmac_desc
  dmaengine: Fix possible use after free
  dmaengine: dw-edma: Add spinlock to protect DONE_INT_MASK and ABORT_INT_MASK
  dmaengine: dw-edma-pcie: Reject devices without driver data
  dmaengine: sh: rz-dmac: Add DMA ACK signal routing support
  irqchip/renesas-rzv2h: Add DMA ACK signal routing support
  dmaengine: dw-edma: Remove dw_edma_add_irq_mask()
  ...
This commit is contained in:
Linus Torvalds
2026-06-23 15:51:14 -07:00
36 changed files with 1408 additions and 938 deletions

View File

@@ -12,6 +12,9 @@ description: |
DMAMUX0 and DMAMUX1, specific DMA request source can only be multiplexed
by any channel of certain group, DMAMUX0 or DMAMUX1, but not both.
This binding has an inverted dma-channel-mask definition compared to
the common DMA binding for historical reasons.
maintainers:
- Peng Fan <peng.fan@nxp.com>
@@ -95,6 +98,12 @@ properties:
eDMA are implemented in big endian mode, otherwise in little mode.
type: boolean
dma-channel-mask:
description: |
Bitmask of available DMA channels (inverted definition).
Bit semantics: 0 means channel available, 1 means channel unavailable
default: 0
required:
- "#dma-cells"
- compatible

View File

@@ -14,17 +14,16 @@ description: |
maintainers:
- Jon Hunter <jonathanh@nvidia.com>
- Rajesh Gumasta <rgumasta@nvidia.com>
allOf:
- $ref: dma-controller.yaml#
- Akhil R <akhilrajeev@nvidia.com>
properties:
compatible:
oneOf:
- const: nvidia,tegra186-gpcdma
- enum:
- nvidia,tegra264-gpcdma
- nvidia,tegra186-gpcdma
- items:
- enum:
- nvidia,tegra264-gpcdma
- nvidia,tegra234-gpcdma
- nvidia,tegra194-gpcdma
- const: nvidia,tegra186-gpcdma
@@ -51,6 +50,14 @@ properties:
iommus:
maxItems: 1
iommu-map:
description:
Maps DMA channel numbers to IOMMU stream IDs. A single entry can map all
channels when stream IDs are contiguous. In systems where the channels or
stream IDs are not contiguous, multiple entries may be needed.
minItems: 1
maxItems: 32
dma-coherent: true
dma-channel-mask:
@@ -60,12 +67,23 @@ required:
- compatible
- reg
- interrupts
- resets
- reset-names
- "#dma-cells"
- iommus
- dma-channel-mask
allOf:
- $ref: dma-controller.yaml#
- if:
properties:
compatible:
contains:
enum:
- nvidia,tegra186-gpcdma
then:
required:
- resets
- reset-names
additionalProperties: false
examples:

View File

@@ -23,6 +23,8 @@ properties:
- qcom,bam-v1.4.0
# MSM8916, SDM630
- qcom,bam-v1.7.0
# Kaanapali
- qcom,bam-v2.0.0
- items:
- enum:
# SDM845, SM6115, SM8150, SM8250 and QCM2290
@@ -118,4 +120,23 @@ examples:
#dma-cells = <1>;
qcom,ee = <0>;
};
- |
#include <dt-bindings/interrupt-controller/arm-gic.h>
soc {
#address-cells = <2>;
#size-cells = <2>;
dma-controller@1dc4000 {
compatible = "qcom,bam-v2.0.0";
reg = <0x0 0x01dc4000 0x0 0x22000>;
interrupts = <GIC_SPI 272 IRQ_TYPE_LEVEL_HIGH>;
#dma-cells = <1>;
iommus = <&apps_smmu 0xc0 0>, <&apps_smmu 0xc1 0>;
qcom,ee = <0>;
qcom,num-ees = <4>;
num-channels = <20>;
qcom,controlled-remotely;
};
};
...

View File

@@ -24,7 +24,9 @@ properties:
- qcom,sm6350-gpi-dma
- items:
- enum:
- qcom,eliza-gpi-dma
- qcom,glymur-gpi-dma
- qcom,hawi-gpi-dma
- qcom,kaanapali-gpi-dma
- qcom,milos-gpi-dma
- qcom,qcm2290-gpi-dma
@@ -35,6 +37,7 @@ properties:
- qcom,sc7280-gpi-dma
- qcom,sc8280xp-gpi-dma
- qcom,sdx75-gpi-dma
- qcom,shikra-gpi-dma
- qcom,sm6115-gpi-dma
- qcom,sm6375-gpi-dma
- qcom,sm8350-gpi-dma

View File

@@ -21,11 +21,12 @@ properties:
- enum:
- snps,axi-dma-1.01a
- intel,kmb-axi-dma
- sophgo,cv1800b-axi-dma
- starfive,jh7110-axi-dma
- starfive,jh8100-axi-dma
- items:
- const: altr,agilex5-axi-dma
- enum:
- altr,agilex5-axi-dma
- sophgo,cv1800b-axi-dma
- const: snps,axi-dma-1.01a
reg:

View File

@@ -14,7 +14,9 @@ allOf:
properties:
compatible:
const: spacemit,k1-pdma
enum:
- spacemit,k1-pdma
- spacemit,k3-pdma
reg:
maxItems: 1

View File

@@ -26697,7 +26697,7 @@ F: sound/soc/codecs/tlv320*.*
F: sound/soc/codecs/tpa6130a2.*
TEXAS INSTRUMENTS DMA DRIVERS
M: Peter Ujfalusi <peter.ujfalusi@gmail.com>
M: Vignesh Raghavendra <vigneshr@ti.com>
L: dmaengine@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/dma/ti-dma-crossbar.txt

View File

@@ -496,6 +496,11 @@ static void msgdma_copy_one(struct msgdma_device *mdev,
{
void __iomem *hw_desc = mdev->desc;
/* Ensure control is the last field — required for correct FIFO flush ordering */
static_assert(offsetof(struct msgdma_extended_desc, control) ==
sizeof(struct msgdma_extended_desc) - sizeof(u32),
"control must be the last field in msgdma_extended_desc");
/*
* Check if the DESC FIFO it not full. If its full, we need to wait
* for at least one entry to become free again
@@ -504,17 +509,18 @@ static void msgdma_copy_one(struct msgdma_device *mdev,
MSGDMA_CSR_STAT_DESC_BUF_FULL)
mdelay(1);
/* Ensure control is the last field — required for correct FIFO flush ordering */
static_assert(offsetof(struct msgdma_extended_desc, control) ==
sizeof(struct msgdma_extended_desc) - sizeof(u32),
"control must be the last field in msgdma_extended_desc");
/*
* The descriptor needs to get copied into the descriptor FIFO
* of the DMA controller. The descriptor will get flushed to the
* FIFO, once the last word (control word) is written. Since we
* are not 100% sure that memcpy() writes all word in the "correct"
* order (address from low to high) on all architectures, we make
* sure this control word is written last by single coding it and
* adding some write-barriers here.
* Copy the descriptor into the descriptor FIFO of the DMA controller,
* excluding the control word. The FIFO is flushed and the descriptor
* becomes valid once the control word is written last.
*/
memcpy((void __force *)hw_desc, &desc->hw_desc,
sizeof(desc->hw_desc) - sizeof(u32));
memcpy_toio(hw_desc, &desc->hw_desc,
offsetof(struct msgdma_extended_desc, control));
/* Write control word last to flush this descriptor into the FIFO */
mdev->idle = false;

View File

@@ -13,6 +13,7 @@
#include <linux/device.h>
#include <linux/dma-mapping.h>
#include <linux/dmaengine.h>
#include <linux/dmapool.h>
#include <linux/err.h>
#include <linux/interrupt.h>
#include <linux/io.h>
@@ -147,6 +148,7 @@ struct axi_dmac_chan {
struct virt_dma_chan vchan;
struct axi_dmac_desc *next_desc;
void *pool;
struct list_head active_descs;
enum dma_transfer_direction direction;
@@ -170,8 +172,6 @@ struct axi_dmac {
void __iomem *base;
int irq;
struct clk *clk;
struct dma_device dma_dev;
struct axi_dmac_chan chan;
};
@@ -650,11 +650,17 @@ static void axi_dmac_issue_pending(struct dma_chan *c)
spin_unlock_irqrestore(&chan->vchan.lock, flags);
}
static void axi_dmac_free_desc(struct axi_dmac_desc *desc)
{
for (unsigned int i = 0; i < desc->num_sgs; i++)
dma_pool_free(desc->chan->pool, desc->sg[i].hw, desc->sg[i].hw_phys);
kfree(desc);
}
static struct axi_dmac_desc *
axi_dmac_alloc_desc(struct axi_dmac_chan *chan, unsigned int num_sgs)
{
struct axi_dmac *dmac = chan_to_axi_dmac(chan);
struct device *dev = dmac->dma_dev.dev;
struct axi_dmac_hw_desc *hws;
struct axi_dmac_desc *desc;
dma_addr_t hw_phys;
@@ -666,22 +672,22 @@ axi_dmac_alloc_desc(struct axi_dmac_chan *chan, unsigned int num_sgs)
desc->num_sgs = num_sgs;
desc->chan = chan;
hws = dma_alloc_coherent(dev, PAGE_ALIGN(num_sgs * sizeof(*hws)),
&hw_phys, GFP_ATOMIC);
if (!hws) {
kfree(desc);
return NULL;
}
for (i = 0; i < num_sgs; i++) {
desc->sg[i].hw = &hws[i];
desc->sg[i].hw_phys = hw_phys + i * sizeof(*hws);
hws = dma_pool_zalloc(chan->pool, GFP_NOWAIT, &hw_phys);
if (!hws) {
desc->num_sgs = i;
axi_dmac_free_desc(desc);
return NULL;
}
hws[i].id = AXI_DMAC_SG_UNUSED;
hws[i].flags = 0;
desc->sg[i].hw = hws;
desc->sg[i].hw_phys = hw_phys;
hws->id = AXI_DMAC_SG_UNUSED;
/* Link hardware descriptors */
hws[i].next_sg_addr = hw_phys + (i + 1) * sizeof(*hws);
if (i)
desc->sg[i - 1].hw->next_sg_addr = hw_phys;
}
/* The last hardware descriptor will trigger an interrupt */
@@ -690,18 +696,6 @@ axi_dmac_alloc_desc(struct axi_dmac_chan *chan, unsigned int num_sgs)
return desc;
}
static void axi_dmac_free_desc(struct axi_dmac_desc *desc)
{
struct axi_dmac *dmac = chan_to_axi_dmac(desc->chan);
struct device *dev = dmac->dma_dev.dev;
struct axi_dmac_hw_desc *hw = desc->sg[0].hw;
dma_addr_t hw_phys = desc->sg[0].hw_phys;
dma_free_coherent(dev, PAGE_ALIGN(desc->num_sgs * sizeof(*hw)),
hw, hw_phys);
kfree(desc);
}
static struct axi_dmac_sg *axi_dmac_fill_linear_sg(struct axi_dmac_chan *chan,
enum dma_transfer_direction direction, dma_addr_t addr,
unsigned int num_periods, unsigned int period_len,
@@ -769,7 +763,7 @@ axi_dmac_prep_peripheral_dma_vec(struct dma_chan *c, const struct dma_vec *vecs,
for (i = 0; i < nb; i++) {
if (!axi_dmac_check_addr(chan, vecs[i].addr) ||
!axi_dmac_check_len(chan, vecs[i].len)) {
kfree(desc);
axi_dmac_free_desc(desc);
return NULL;
}
@@ -935,9 +929,26 @@ static struct dma_async_tx_descriptor *axi_dmac_prep_interleaved(
return vchan_tx_prep(&chan->vchan, &desc->vdesc, flags);
}
static int axi_dmac_alloc_chan_resources(struct dma_chan *c)
{
struct axi_dmac_chan *chan = to_axi_dmac_chan(c);
struct device *dev = c->device->dev;
chan->pool = dma_pool_create(dev_name(dev), dev,
sizeof(struct axi_dmac_hw_desc),
__alignof__(struct axi_dmac_hw_desc), 0);
if (!chan->pool)
return -ENOMEM;
return 0;
}
static void axi_dmac_free_chan_resources(struct dma_chan *c)
{
struct axi_dmac_chan *chan = to_axi_dmac_chan(c);
vchan_free_chan_resources(to_virt_chan(c));
dma_pool_destroy(chan->pool);
}
static void axi_dmac_desc_free(struct virt_dma_desc *vdesc)
@@ -1198,6 +1209,7 @@ static int axi_dmac_probe(struct platform_device *pdev)
{
struct dma_device *dma_dev;
struct axi_dmac *dmac;
struct clk *clk;
struct regmap *regmap;
unsigned int version;
u32 irq_mask = 0;
@@ -1217,9 +1229,9 @@ static int axi_dmac_probe(struct platform_device *pdev)
if (IS_ERR(dmac->base))
return PTR_ERR(dmac->base);
dmac->clk = devm_clk_get_enabled(&pdev->dev, NULL);
if (IS_ERR(dmac->clk))
return PTR_ERR(dmac->clk);
clk = devm_clk_get_enabled(&pdev->dev, NULL);
if (IS_ERR(clk))
return PTR_ERR(clk);
version = axi_dmac_read(dmac, ADI_AXI_REG_VERSION);
@@ -1239,6 +1251,7 @@ static int axi_dmac_probe(struct platform_device *pdev)
dma_cap_set(DMA_SLAVE, dma_dev->cap_mask);
dma_cap_set(DMA_CYCLIC, dma_dev->cap_mask);
dma_cap_set(DMA_INTERLEAVE, dma_dev->cap_mask);
dma_dev->device_alloc_chan_resources = axi_dmac_alloc_chan_resources;
dma_dev->device_free_chan_resources = axi_dmac_free_chan_resources;
dma_dev->device_tx_status = dma_cookie_status;
dma_dev->device_issue_pending = axi_dmac_issue_pending;

View File

@@ -905,11 +905,12 @@ void dma_release_channel(struct dma_chan *chan)
mutex_lock(&dma_list_mutex);
WARN_ONCE(chan->client_count != 1,
"chan reference count %d != 1\n", chan->client_count);
dma_chan_put(chan);
/* drop PRIVATE cap enabled by __dma_request_channel() */
if (--chan->device->privatecnt == 0)
dma_cap_clear(DMA_PRIVATE, chan->device->cap_mask);
dma_chan_put(chan);
if (chan->slave) {
sysfs_remove_link(&chan->dev->device.kobj, DMA_SLAVE_NAME);
sysfs_remove_link(&chan->slave->kobj, chan->name);

View File

@@ -137,7 +137,7 @@ struct dmatest_params {
* @did_init: module has been initialized completely
* @last_error: test has faced configuration issues
*/
static struct dmatest_info {
struct dmatest_info {
/* Test parameters */
struct dmatest_params params;
@@ -147,7 +147,9 @@ static struct dmatest_info {
int last_error;
struct mutex lock;
bool did_init;
} test_info = {
};
static struct dmatest_info test_info = {
.channels = LIST_HEAD_INIT(test_info.channels),
.lock = __MUTEX_INITIALIZER(test_info.lock),
};

View File

@@ -437,8 +437,6 @@ static void axi_chan_block_xfer_start(struct axi_dma_chan *chan,
return;
}
axi_dma_enable(chan->chip);
config.dst_multblk_type = DWAXIDMAC_MBLK_TYPE_LL;
config.src_multblk_type = DWAXIDMAC_MBLK_TYPE_LL;
config.tt_fc = DWAXIDMAC_TT_FC_MEM_TO_MEM_DMAC;
@@ -518,11 +516,17 @@ static void dw_axi_dma_synchronize(struct dma_chan *dchan)
static int dma_chan_alloc_chan_resources(struct dma_chan *dchan)
{
struct axi_dma_chan *chan = dchan_to_axi_dma_chan(dchan);
int ret;
ret = pm_runtime_resume_and_get(chan->chip->dev);
if (ret < 0)
return ret;
/* ASSERT: channel is idle */
if (axi_chan_is_hw_enable(chan)) {
dev_err(chan2dev(chan), "%s is non-idle!\n",
axi_chan_name(chan));
pm_runtime_put(chan->chip->dev);
return -EBUSY;
}
@@ -533,12 +537,11 @@ static int dma_chan_alloc_chan_resources(struct dma_chan *dchan)
64, 0);
if (!chan->desc_pool) {
dev_err(chan2dev(chan), "No memory for descriptors\n");
pm_runtime_put(chan->chip->dev);
return -ENOMEM;
}
dev_vdbg(dchan2dev(dchan), "%s: allocating\n", axi_chan_name(chan));
pm_runtime_get(chan->chip->dev);
return 0;
}
@@ -1665,6 +1668,8 @@ static void dw_remove(struct platform_device *pdev)
}
static const struct dev_pm_ops dw_axi_dma_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
pm_runtime_force_resume)
SET_RUNTIME_PM_OPS(axi_dma_runtime_suspend, axi_dma_runtime_resume, NULL)
};

View File

@@ -988,20 +988,12 @@ static inline void dw_edma_dec_irq_alloc(int *nr_irqs, u32 *alloc, u16 cnt)
}
}
static inline void dw_edma_add_irq_mask(u32 *mask, u32 alloc, u16 cnt)
{
while (*mask * alloc < cnt)
(*mask)++;
}
static int dw_edma_irq_request(struct dw_edma *dw,
u32 *wr_alloc, u32 *rd_alloc)
{
struct dw_edma_chip *chip = dw->chip;
struct device *dev = dw->chip->dev;
struct msi_desc *msi_desc;
u32 wr_mask = 1;
u32 rd_mask = 1;
int i, err = 0;
u32 ch_cnt;
int irq;
@@ -1038,9 +1030,6 @@ static int dw_edma_irq_request(struct dw_edma *dw,
dw_edma_dec_irq_alloc(&tmp, rd_alloc, dw->rd_ch_cnt);
}
dw_edma_add_irq_mask(&wr_mask, *wr_alloc, dw->wr_ch_cnt);
dw_edma_add_irq_mask(&rd_mask, *rd_alloc, dw->rd_ch_cnt);
for (i = 0; i < (*wr_alloc + *rd_alloc); i++) {
irq = chip->ops->irq_vector(dev, i);
err = request_irq(irq,

View File

@@ -109,7 +109,7 @@ struct dw_edma {
struct dw_edma_chan *chan;
raw_spinlock_t lock; /* Only for legacy */
raw_spinlock_t lock; /* Protect v0 shared registers */
struct dw_edma_chip *chip;

View File

@@ -27,6 +27,7 @@
/* AMD MDB (Xilinx) specific defines */
#define PCI_DEVICE_ID_XILINX_B054 0xb054
#define PCI_DEVICE_ID_XILINX_B00F 0xb00f
#define DW_PCIE_XILINX_MDB_VSEC_DMA_ID 0x6
#define DW_PCIE_XILINX_MDB_VSEC_ID 0x20
@@ -125,6 +126,19 @@ static const struct dw_edma_pcie_data xilinx_mdb_data = {
.rd_ch_cnt = 8,
};
static const struct dw_edma_pcie_data xilinx_cpm6_dma_data = {
/* MDB registers location */
.rg.bar = BAR_0,
.rg.off = SZ_4K, /* 4 Kbytes */
.rg.sz = SZ_8K, /* 8 Kbytes */
/* Other */
.mf = EDMA_MF_HDMA_NATIVE,
.irqs = 1,
.wr_ch_cnt = 8,
.rd_ch_cnt = 8,
};
static void dw_edma_set_chan_region_offset(struct dw_edma_pcie_data *pdata,
enum pci_barno bar, off_t start_off,
off_t ll_off_gap, size_t ll_size,
@@ -314,6 +328,9 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev,
int i, mask;
bool non_ll = false;
if (!pdata)
return -ENODEV;
struct dw_edma_pcie_data *vsec_data __free(kfree) =
kmalloc_obj(*vsec_data);
if (!vsec_data)
@@ -547,6 +564,8 @@ static const struct pci_device_id dw_edma_pcie_id_table[] = {
{ PCI_DEVICE_DATA(SYNOPSYS, EDDA, &snps_edda_data) },
{ PCI_VDEVICE(XILINX, PCI_DEVICE_ID_XILINX_B054),
(kernel_ulong_t)&xilinx_mdb_data },
{ PCI_VDEVICE(XILINX, PCI_DEVICE_ID_XILINX_B00F),
.driver_data = (kernel_ulong_t)&xilinx_cpm6_dma_data },
{ }
};
MODULE_DEVICE_TABLE(pci, dw_edma_pcie_id_table);

View File

@@ -364,6 +364,7 @@ static void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first)
{
struct dw_edma_chan *chan = chunk->chan;
struct dw_edma *dw = chan->dw;
unsigned long flags;
u32 tmp;
dw_edma_v0_core_write_chunk(chunk);
@@ -408,6 +409,8 @@ static void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first)
}
}
/* Interrupt unmask - done, abort */
raw_spin_lock_irqsave(&dw->lock, flags);
tmp = GET_RW_32(dw, chan->dir, int_mask);
tmp &= ~FIELD_PREP(EDMA_V0_DONE_INT_MASK, BIT(chan->id));
tmp &= ~FIELD_PREP(EDMA_V0_ABORT_INT_MASK, BIT(chan->id));
@@ -416,6 +419,9 @@ static void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first)
tmp = GET_RW_32(dw, chan->dir, linked_list_err_en);
tmp |= FIELD_PREP(EDMA_V0_LINKED_LIST_ERR_MASK, BIT(chan->id));
SET_RW_32(dw, chan->dir, linked_list_err_en, tmp);
raw_spin_unlock_irqrestore(&dw->lock, flags);
/* Channel control */
SET_CH_32(dw, chan->dir, chan->id, ch_control1,
(DW_EDMA_V0_CCS | DW_EDMA_V0_LLE));

View File

@@ -1587,18 +1587,11 @@ static const struct of_device_id ep93xx_dma_of_ids[] = {
};
MODULE_DEVICE_TABLE(of, ep93xx_dma_of_ids);
static const struct platform_device_id ep93xx_dma_driver_ids[] = {
{ "ep93xx-dma-m2p", 0 },
{ "ep93xx-dma-m2m", 1 },
{ },
};
static struct platform_driver ep93xx_dma_driver = {
.driver = {
.name = "ep93xx-dma",
.of_match_table = ep93xx_dma_of_ids,
},
.id_table = ep93xx_dma_driver_ids,
.probe = ep93xx_dma_probe,
};

View File

@@ -1037,6 +1037,7 @@ static const struct pci_device_id hisi_dma_pci_tbl[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_HUAWEI, 0xa122) },
{ 0, }
};
MODULE_DEVICE_TABLE(pci, hisi_dma_pci_tbl);
static struct pci_driver hisi_dma_pci_driver = {
.name = "hisi_dma",
@@ -1050,4 +1051,3 @@ MODULE_AUTHOR("Zhou Wang <wangzhou1@hisilicon.com>");
MODULE_AUTHOR("Zhenfa Qiu <qiuzhenfa@hisilicon.com>");
MODULE_DESCRIPTION("HiSilicon Kunpeng DMA controller driver");
MODULE_LICENSE("GPL v2");
MODULE_DEVICE_TABLE(pci, hisi_dma_pci_tbl);

View File

@@ -2364,7 +2364,9 @@ static int sdma_probe(struct platform_device *pdev)
return dev_err_probe(&pdev->dev, ret,
"failed to register controller\n");
spba_bus = of_find_compatible_node(NULL, NULL, "fsl,spba-bus");
struct device_node *sdma_parent_np __free(device_node) = of_get_parent(np);
spba_bus = of_get_compatible_child(sdma_parent_np, "fsl,spba-bus");
ret = of_address_to_resource(spba_bus, 0, &spba_res);
if (!ret) {
sdma->spba_start_addr = spba_res.start;

View File

@@ -51,7 +51,10 @@
#define DCSR_CMPST BIT(10) /* The Descriptor Compare Status */
#define DCSR_EORINTR BIT(9) /* The end of Receive */
#define DRCMR(n) ((((n) < 64) ? 0x0100 : 0x1100) + (((n) & 0x3f) << 2))
#define DRCMR_BASE 0x0100
#define DRCMR_EXT_BASE_K3 0x1000
#define DRCMR_EXT_BASE_DEFAULT 0x1100
#define DRCMR_REQ_LIMIT 64
#define DRCMR_MAPVLD BIT(7) /* Map Valid (read / write) */
#define DRCMR_CHLNUM 0x1f /* mask for Channel Number (read / write) */
@@ -154,6 +157,7 @@ struct mmp_pdma_phy {
* @run_bits: Control bits in DCSR register for channel start/stop
* @dma_width: DMA addressing width in bits (32 or 64). Determines the
* DMA mask capability of the controller hardware.
* @drcmr_ext_base: Base DRCMR address for extended requests
*/
struct mmp_pdma_ops {
/* Hardware Register Operations */
@@ -174,6 +178,7 @@ struct mmp_pdma_ops {
/* Controller Configuration */
u32 run_bits;
u32 dma_width;
u32 drcmr_ext_base;
};
struct mmp_pdma_device {
@@ -195,6 +200,13 @@ struct mmp_pdma_device {
#define to_mmp_pdma_dev(dmadev) \
container_of(dmadev, struct mmp_pdma_device, device)
static u32 mmp_pdma_get_drcmr(struct mmp_pdma_device *pdev, u32 drcmr)
{
if (drcmr < DRCMR_REQ_LIMIT)
return DRCMR_BASE + (drcmr << 2);
return pdev->ops->drcmr_ext_base + ((drcmr - DRCMR_REQ_LIMIT) << 2);
}
/* For 32-bit PDMA */
static void write_next_addr_32(struct mmp_pdma_phy *phy, dma_addr_t addr)
{
@@ -301,7 +313,7 @@ static void enable_chan(struct mmp_pdma_phy *phy)
pdev = to_mmp_pdma_dev(phy->vchan->chan.device);
reg = DRCMR(phy->vchan->drcmr);
reg = mmp_pdma_get_drcmr(pdev, phy->vchan->drcmr);
writel(DRCMR_MAPVLD | phy->idx, phy->base + reg);
dalgn = readl(phy->base + DALGN);
@@ -437,7 +449,7 @@ static void mmp_pdma_free_phy(struct mmp_pdma_chan *pchan)
return;
/* clear the channel mapping in DRCMR */
reg = DRCMR(pchan->drcmr);
reg = mmp_pdma_get_drcmr(pdev, pchan->drcmr);
writel(0, pchan->phy->base + reg);
spin_lock_irqsave(&pdev->phy_lock, flags);
@@ -1179,6 +1191,7 @@ static const struct mmp_pdma_ops marvell_pdma_v1_ops = {
.get_desc_dst_addr = get_desc_dst_addr_32,
.run_bits = (DCSR_RUN),
.dma_width = 32,
.drcmr_ext_base = DRCMR_EXT_BASE_DEFAULT,
};
static const struct mmp_pdma_ops spacemit_k1_pdma_ops = {
@@ -1192,6 +1205,21 @@ static const struct mmp_pdma_ops spacemit_k1_pdma_ops = {
.get_desc_dst_addr = get_desc_dst_addr_64,
.run_bits = (DCSR_RUN | DCSR_LPAEEN),
.dma_width = 64,
.drcmr_ext_base = DRCMR_EXT_BASE_DEFAULT,
};
static const struct mmp_pdma_ops spacemit_k3_pdma_ops = {
.write_next_addr = write_next_addr_64,
.read_src_addr = read_src_addr_64,
.read_dst_addr = read_dst_addr_64,
.set_desc_next_addr = set_desc_next_addr_64,
.set_desc_src_addr = set_desc_src_addr_64,
.set_desc_dst_addr = set_desc_dst_addr_64,
.get_desc_src_addr = get_desc_src_addr_64,
.get_desc_dst_addr = get_desc_dst_addr_64,
.run_bits = (DCSR_RUN | DCSR_LPAEEN | DCSR_EORIRQEN | DCSR_EORSTOPEN),
.dma_width = 64,
.drcmr_ext_base = DRCMR_EXT_BASE_K3,
};
static const struct of_device_id mmp_pdma_dt_ids[] = {
@@ -1201,6 +1229,9 @@ static const struct of_device_id mmp_pdma_dt_ids[] = {
}, {
.compatible = "spacemit,k1-pdma",
.data = &spacemit_k1_pdma_ops
}, {
.compatible = "spacemit,k3-pdma",
.data = &spacemit_k3_pdma_ops
}, {
/* sentinel */
}

View File

@@ -1486,20 +1486,6 @@ static void nbpf_remove(struct platform_device *pdev)
clk_disable_unprepare(nbpf->clk);
}
static const struct platform_device_id nbpf_ids[] = {
{"nbpfaxi64dmac1b4", (kernel_ulong_t)&nbpf_cfg[NBPF1B4]},
{"nbpfaxi64dmac1b8", (kernel_ulong_t)&nbpf_cfg[NBPF1B8]},
{"nbpfaxi64dmac1b16", (kernel_ulong_t)&nbpf_cfg[NBPF1B16]},
{"nbpfaxi64dmac4b4", (kernel_ulong_t)&nbpf_cfg[NBPF4B4]},
{"nbpfaxi64dmac4b8", (kernel_ulong_t)&nbpf_cfg[NBPF4B8]},
{"nbpfaxi64dmac4b16", (kernel_ulong_t)&nbpf_cfg[NBPF4B16]},
{"nbpfaxi64dmac8b4", (kernel_ulong_t)&nbpf_cfg[NBPF8B4]},
{"nbpfaxi64dmac8b8", (kernel_ulong_t)&nbpf_cfg[NBPF8B8]},
{"nbpfaxi64dmac8b16", (kernel_ulong_t)&nbpf_cfg[NBPF8B16]},
{},
};
MODULE_DEVICE_TABLE(platform, nbpf_ids);
static int nbpf_runtime_suspend(struct device *dev)
{
struct nbpf_device *nbpf = dev_get_drvdata(dev);
@@ -1523,7 +1509,6 @@ static struct platform_driver nbpf_driver = {
.of_match_table = nbpf_match,
.pm = pm_ptr(&nbpf_pm_ops),
},
.id_table = nbpf_ids,
.probe = nbpf_probe,
.remove = nbpf_remove,
};

View File

@@ -970,6 +970,7 @@ static const struct pci_device_id pch_dma_id_table[] = {
{ PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7831_DMA2_4CH), 4}, /* SPI */
{ 0, },
};
MODULE_DEVICE_TABLE(pci, pch_dma_id_table);
static SIMPLE_DEV_PM_OPS(pch_dma_pm_ops, pch_dma_suspend, pch_dma_resume);
@@ -987,4 +988,3 @@ MODULE_DESCRIPTION("Intel EG20T PCH / LAPIS Semicon ML7213/ML7223/ML7831 IOH "
"DMA controller driver");
MODULE_AUTHOR("Yong Wang <yong.y.wang@intel.com>");
MODULE_LICENSE("GPL v2");
MODULE_DEVICE_TABLE(pci, pch_dma_id_table);

View File

@@ -11,7 +11,7 @@ config QCOM_ADM
and on-chip peripheral devices.
config QCOM_BAM_DMA
tristate "QCOM BAM DMA support"
tristate "Qualcomm BAM DMA support"
depends on ARCH_QCOM || (COMPILE_TEST && OF && ARM)
select DMA_ENGINE
select DMA_VIRTUAL_CHANNELS
@@ -20,7 +20,7 @@ config QCOM_BAM_DMA
provides DMA capabilities for a variety of on-chip devices.
config QCOM_GPI_DMA
tristate "Qualcomm Technologies GPI DMA support"
tristate "Qualcomm GPI DMA support"
depends on ARCH_QCOM
select DMA_ENGINE
select DMA_VIRTUAL_CHANNELS
@@ -32,7 +32,7 @@ config QCOM_GPI_DMA
transfer data between DDR and peripheral.
config QCOM_HIDMA_MGMT
tristate "Qualcomm Technologies HIDMA Management support"
tristate "Qualcomm HIDMA Management support"
depends on HAS_IOMEM
select DMA_ENGINE
help
@@ -44,7 +44,7 @@ config QCOM_HIDMA_MGMT
host would run the QCOM_HIDMA_MGMT management driver.
config QCOM_HIDMA
tristate "Qualcomm Technologies HIDMA Channel support"
tristate "Qualcomm HIDMA Channel support"
depends on HAS_IOMEM
select DMA_ENGINE
help

View File

@@ -199,6 +199,35 @@ static const struct reg_offset_data bam_v1_7_reg_info[] = {
[BAM_P_FIFO_SIZES] = { 0x13820, 0x00, 0x1000, 0x00 },
};
static const struct reg_offset_data bam_v2_0_reg_info[] = {
[BAM_CTRL] = { 0x0000, 0x00, 0x00, 0x00 },
[BAM_REVISION] = { 0x1000, 0x00, 0x00, 0x00 },
[BAM_NUM_PIPES] = { 0x1008, 0x00, 0x00, 0x00 },
[BAM_DESC_CNT_TRSHLD] = { 0x0008, 0x00, 0x00, 0x00 },
[BAM_IRQ_SRCS] = { 0x3010, 0x00, 0x00, 0x00 },
[BAM_IRQ_SRCS_MSK] = { 0x3014, 0x00, 0x00, 0x00 },
[BAM_IRQ_SRCS_UNMASKED] = { 0x3018, 0x00, 0x00, 0x00 },
[BAM_IRQ_STTS] = { 0x0014, 0x00, 0x00, 0x00 },
[BAM_IRQ_CLR] = { 0x0018, 0x00, 0x00, 0x00 },
[BAM_IRQ_EN] = { 0x001C, 0x00, 0x00, 0x00 },
[BAM_CNFG_BITS] = { 0x007C, 0x00, 0x00, 0x00 },
[BAM_IRQ_SRCS_EE] = { 0x3000, 0x00, 0x00, 0x1000 },
[BAM_IRQ_SRCS_MSK_EE] = { 0x3004, 0x00, 0x00, 0x1000 },
[BAM_P_CTRL] = { 0xC000, 0x1000, 0x00, 0x00 },
[BAM_P_RST] = { 0xC004, 0x1000, 0x00, 0x00 },
[BAM_P_HALT] = { 0xC008, 0x1000, 0x00, 0x00 },
[BAM_P_IRQ_STTS] = { 0xC010, 0x1000, 0x00, 0x00 },
[BAM_P_IRQ_CLR] = { 0xC014, 0x1000, 0x00, 0x00 },
[BAM_P_IRQ_EN] = { 0xC018, 0x1000, 0x00, 0x00 },
[BAM_P_EVNT_DEST_ADDR] = { 0xC82C, 0x00, 0x1000, 0x00 },
[BAM_P_EVNT_REG] = { 0xC818, 0x00, 0x1000, 0x00 },
[BAM_P_SW_OFSTS] = { 0xC800, 0x00, 0x1000, 0x00 },
[BAM_P_DATA_FIFO_ADDR] = { 0xC824, 0x00, 0x1000, 0x00 },
[BAM_P_DESC_FIFO_ADDR] = { 0xC81C, 0x00, 0x1000, 0x00 },
[BAM_P_EVNT_GEN_TRSHLD] = { 0xC828, 0x00, 0x1000, 0x00 },
[BAM_P_FIFO_SIZES] = { 0xC820, 0x00, 0x1000, 0x00 },
};
/* BAM CTRL */
#define BAM_SW_RST BIT(0)
#define BAM_EN BIT(1)
@@ -1208,6 +1237,7 @@ static const struct of_device_id bam_of_match[] = {
{ .compatible = "qcom,bam-v1.3.0", .data = &bam_v1_3_reg_info },
{ .compatible = "qcom,bam-v1.4.0", .data = &bam_v1_4_reg_info },
{ .compatible = "qcom,bam-v1.7.0", .data = &bam_v1_7_reg_info },
{ .compatible = "qcom,bam-v2.0.0", .data = &bam_v2_0_reg_info },
{}
};

View File

@@ -2260,6 +2260,7 @@ static int gpi_probe(struct platform_device *pdev)
/* clear and Set capabilities */
dma_cap_zero(gpi_dev->dma_device.cap_mask);
dma_cap_set(DMA_SLAVE, gpi_dev->dma_device.cap_mask);
dma_cap_set(DMA_PRIVATE, gpi_dev->dma_device.cap_mask);
/* configure dmaengine apis */
gpi_dev->dma_device.directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV);

View File

@@ -624,12 +624,10 @@ static ssize_t hidma_show_values(struct device *dev,
{
struct hidma_dev *mdev = dev_get_drvdata(dev);
buf[0] = 0;
if (strcmp(attr->attr.name, "chid") == 0)
sprintf(buf, "%d\n", mdev->chidx);
return sysfs_emit(buf, "%d\n", mdev->chidx);
return strlen(buf);
return 0;
}
static inline void hidma_sysfs_uninit(struct hidma_dev *dev)

View File

@@ -102,15 +102,12 @@ static ssize_t show_values(struct device *dev, struct device_attribute *attr,
struct hidma_mgmt_dev *mdev = dev_get_drvdata(dev);
unsigned int i;
buf[0] = 0;
for (i = 0; i < ARRAY_SIZE(hidma_mgmt_files); i++) {
if (strcmp(attr->attr.name, hidma_mgmt_files[i].name) == 0) {
sprintf(buf, "%d\n", hidma_mgmt_files[i].get(mdev));
break;
}
if (strcmp(attr->attr.name, hidma_mgmt_files[i].name) == 0)
return sysfs_emit(buf, "%d\n",
hidma_mgmt_files[i].get(mdev));
}
return strlen(buf);
return 0;
}
static ssize_t set_values(struct device *dev, struct device_attribute *attr,
@@ -143,15 +140,15 @@ static ssize_t show_values_channel(struct kobject *kobj,
struct hidma_chan_attr *chattr;
struct hidma_mgmt_dev *mdev;
buf[0] = 0;
chattr = container_of(attr, struct hidma_chan_attr, attr);
mdev = chattr->mdev;
if (strcmp(attr->attr.name, "priority") == 0)
sprintf(buf, "%d\n", mdev->priority[chattr->index]);
else if (strcmp(attr->attr.name, "weight") == 0)
sprintf(buf, "%d\n", mdev->weight[chattr->index]);
return strlen(buf);
if (strcmp(attr->attr.name, "priority") == 0)
return sysfs_emit(buf, "%d\n", mdev->priority[chattr->index]);
else if (strcmp(attr->attr.name, "weight") == 0)
return sysfs_emit(buf, "%d\n", mdev->weight[chattr->index]);
return 0;
}
static ssize_t set_values_channel(struct kobject *kobj,

File diff suppressed because it is too large Load Diff

View File

@@ -602,7 +602,6 @@ struct d40_base {
struct dma_device dma_both;
struct dma_device dma_slave;
struct dma_device dma_memcpy;
struct d40_chan *phy_chans;
struct d40_chan *log_chans;
struct d40_chan **lookup_log_chans;
struct d40_chan **lookup_phy_chans;
@@ -621,6 +620,7 @@ struct d40_base {
u32 *regs_interrupt;
u16 gcc_pwr_off_mask;
struct d40_gen_dmac gen_dmac;
struct d40_chan phy_chans[];
};
static struct device *chan2dev(struct d40_chan *d40c)
@@ -3128,6 +3128,7 @@ static int __init d40_hw_detect_init(struct platform_device *pdev,
struct clk *clk;
void __iomem *virtbase;
struct d40_base *base;
size_t alloc_size;
int num_log_chans;
int num_phy_chans;
int num_memcpy_chans;
@@ -3185,22 +3186,24 @@ static int __init d40_hw_detect_init(struct platform_device *pdev,
else
num_phy_chans = 4 * (readl(virtbase + D40_DREG_ICFG) & 0x7) + 4;
num_phy_chans = min(num_phy_chans, STEDMA40_MAX_PHYS);
/* The number of channels used for memcpy */
if (plat_data->num_of_memcpy_chans)
num_memcpy_chans = plat_data->num_of_memcpy_chans;
else
num_memcpy_chans = ARRAY_SIZE(dma40_memcpy_channels);
num_memcpy_chans = min(num_memcpy_chans, D40_MEMCPY_MAX_CHANS);
num_log_chans = num_phy_chans * D40_MAX_LOG_CHAN_PER_PHY;
dev_info(dev,
"hardware rev: %d with %d physical and %d logical channels\n",
rev, num_phy_chans, num_log_chans);
base = devm_kzalloc(dev,
ALIGN(sizeof(struct d40_base), 4) +
(num_phy_chans + num_log_chans + num_memcpy_chans) *
sizeof(struct d40_chan), GFP_KERNEL);
alloc_size = struct_size(base, phy_chans, num_phy_chans);
alloc_size += sizeof(*base->log_chans) * (num_log_chans + num_memcpy_chans);
base = devm_kzalloc(dev, alloc_size, GFP_KERNEL);
if (!base)
return -ENOMEM;
@@ -3213,7 +3216,6 @@ static int __init d40_hw_detect_init(struct platform_device *pdev,
base->virtbase = virtbase;
base->plat_data = plat_data;
base->dev = dev;
base->phy_chans = ((void *)base) + ALIGN(sizeof(struct d40_base), 4);
base->log_chans = &base->phy_chans[num_phy_chans];
if (base->plat_data->num_of_phy_chans == 14) {

View File

@@ -15,6 +15,7 @@
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_dma.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/reset.h>
#include <linux/slab.h>
@@ -22,7 +23,6 @@
#include "virt-dma.h"
/* CSR register */
#define TEGRA_GPCDMA_CHAN_CSR 0x00
#define TEGRA_GPCDMA_CSR_ENB BIT(31)
#define TEGRA_GPCDMA_CSR_IE_EOC BIT(30)
#define TEGRA_GPCDMA_CSR_ONCE BIT(27)
@@ -58,7 +58,6 @@
#define TEGRA_GPCDMA_CSR_WEIGHT GENMASK(13, 10)
/* STATUS register */
#define TEGRA_GPCDMA_CHAN_STATUS 0x004
#define TEGRA_GPCDMA_STATUS_BUSY BIT(31)
#define TEGRA_GPCDMA_STATUS_ISE_EOC BIT(30)
#define TEGRA_GPCDMA_STATUS_PING_PONG BIT(28)
@@ -70,22 +69,13 @@
#define TEGRA_GPCDMA_STATUS_IRQ_STA BIT(21)
#define TEGRA_GPCDMA_STATUS_IRQ_TRIG_STA BIT(20)
#define TEGRA_GPCDMA_CHAN_CSRE 0x008
#define TEGRA_GPCDMA_CHAN_CSRE_PAUSE BIT(31)
/* Source address */
#define TEGRA_GPCDMA_CHAN_SRC_PTR 0x00C
/* Destination address */
#define TEGRA_GPCDMA_CHAN_DST_PTR 0x010
/* High address pointer */
#define TEGRA_GPCDMA_CHAN_HIGH_ADDR_PTR 0x014
#define TEGRA_GPCDMA_HIGH_ADDR_SRC_PTR GENMASK(7, 0)
#define TEGRA_GPCDMA_HIGH_ADDR_DST_PTR GENMASK(23, 16)
/* MC sequence register */
#define TEGRA_GPCDMA_CHAN_MCSEQ 0x18
#define TEGRA_GPCDMA_MCSEQ_DATA_SWAP BIT(31)
#define TEGRA_GPCDMA_MCSEQ_REQ_COUNT GENMASK(30, 25)
#define TEGRA_GPCDMA_MCSEQ_BURST GENMASK(24, 23)
@@ -101,7 +91,6 @@
#define TEGRA_GPCDMA_MCSEQ_STREAM_ID0_MASK GENMASK(6, 0)
/* MMIO sequence register */
#define TEGRA_GPCDMA_CHAN_MMIOSEQ 0x01c
#define TEGRA_GPCDMA_MMIOSEQ_DBL_BUF BIT(31)
#define TEGRA_GPCDMA_MMIOSEQ_BUS_WIDTH GENMASK(30, 28)
#define TEGRA_GPCDMA_MMIOSEQ_BUS_WIDTH_8 \
@@ -120,17 +109,7 @@
#define TEGRA_GPCDMA_MMIOSEQ_WRAP_WORD GENMASK(18, 16)
#define TEGRA_GPCDMA_MMIOSEQ_MMIO_PROT GENMASK(8, 7)
/* Channel WCOUNT */
#define TEGRA_GPCDMA_CHAN_WCOUNT 0x20
/* Transfer count */
#define TEGRA_GPCDMA_CHAN_XFER_COUNT 0x24
/* DMA byte count status */
#define TEGRA_GPCDMA_CHAN_DMA_BYTE_STATUS 0x28
/* Error Status Register */
#define TEGRA_GPCDMA_CHAN_ERR_STATUS 0x30
#define TEGRA_GPCDMA_CHAN_ERR_TYPE_SHIFT 8
#define TEGRA_GPCDMA_CHAN_ERR_TYPE_MASK 0xF
#define TEGRA_GPCDMA_CHAN_ERR_TYPE(err) ( \
@@ -143,16 +122,6 @@
#define TEGRA_DMA_MC_SLAVE_ERR 0xB
#define TEGRA_DMA_MMIO_SLAVE_ERR 0xA
/* Fixed Pattern */
#define TEGRA_GPCDMA_CHAN_FIXED_PATTERN 0x34
#define TEGRA_GPCDMA_CHAN_TZ 0x38
#define TEGRA_GPCDMA_CHAN_TZ_MMIO_PROT_1 BIT(0)
#define TEGRA_GPCDMA_CHAN_TZ_MC_PROT_1 BIT(1)
#define TEGRA_GPCDMA_CHAN_SPARE 0x3c
#define TEGRA_GPCDMA_CHAN_SPARE_EN_LEGACY_FC BIT(16)
/*
* If any burst is in flight and DMA paused then this is the time to complete
* on-flight burst and update DMA status register.
@@ -178,21 +147,30 @@ struct tegra_dma_channel;
*/
struct tegra_dma_chip_data {
bool hw_support_pause;
unsigned int addr_bits;
unsigned int nr_channels;
unsigned int channel_reg_size;
unsigned int max_dma_count;
const struct tegra_dma_channel_regs *channel_regs;
int (*terminate)(struct tegra_dma_channel *tdc);
};
/* DMA channel registers */
struct tegra_dma_channel_regs {
u32 csr;
u32 src_ptr;
u32 dst_ptr;
u32 high_addr_ptr;
u32 status;
u32 csre;
u32 src;
u32 dst;
u32 high_addr;
u32 src_high;
u32 dst_high;
u32 mc_seq;
u32 mmio_seq;
u32 wcount;
u32 wxfer;
u32 wstatus;
u32 err_status;
u32 fixed_pattern;
};
@@ -205,7 +183,13 @@ struct tegra_dma_channel_regs {
*/
struct tegra_dma_sg_req {
unsigned int len;
struct tegra_dma_channel_regs ch_regs;
dma_addr_t src;
dma_addr_t dst;
u32 csr;
u32 mc_seq;
u32 mmio_seq;
u32 wcount;
u32 fixed_pattern;
};
/*
@@ -228,19 +212,20 @@ struct tegra_dma_desc {
* tegra_dma_channel: Channel specific information
*/
struct tegra_dma_channel {
bool config_init;
char name[30];
enum dma_transfer_direction sid_dir;
enum dma_status status;
int id;
int irq;
int slave_id;
const struct tegra_dma_channel_regs *regs;
struct tegra_dma *tdma;
struct virt_dma_chan vc;
struct tegra_dma_desc *dma_desc;
struct dma_slave_config dma_sconfig;
enum dma_transfer_direction sid_dir;
enum dma_status status;
unsigned int stream_id;
unsigned long chan_base_offset;
bool config_init;
char name[30];
int id;
int irq;
int slave_id;
};
/*
@@ -284,26 +269,55 @@ static inline struct device *tdc2dev(struct tegra_dma_channel *tdc)
return tdc->vc.chan.device->dev;
}
static void tegra_dma_program_addr(struct tegra_dma_channel *tdc,
struct tegra_dma_sg_req *sg_req)
{
tdc_write(tdc, tdc->regs->src, lower_32_bits(sg_req->src));
tdc_write(tdc, tdc->regs->dst, lower_32_bits(sg_req->dst));
if (tdc->tdma->chip_data->addr_bits > 39) {
tdc_write(tdc, tdc->regs->src_high, upper_32_bits(sg_req->src));
tdc_write(tdc, tdc->regs->dst_high, upper_32_bits(sg_req->dst));
} else {
u32 src_high = FIELD_PREP(TEGRA_GPCDMA_HIGH_ADDR_SRC_PTR,
upper_32_bits(sg_req->src));
u32 dst_high = FIELD_PREP(TEGRA_GPCDMA_HIGH_ADDR_DST_PTR,
upper_32_bits(sg_req->dst));
tdc_write(tdc, tdc->regs->high_addr, src_high | dst_high);
}
}
static void tegra_dma_dump_chan_regs(struct tegra_dma_channel *tdc)
{
dev_dbg(tdc2dev(tdc), "DMA Channel %d name %s register dump:\n",
tdc->id, tdc->name);
dev_dbg(tdc2dev(tdc), "CSR %x STA %x CSRE %x SRC %x DST %x\n",
tdc_read(tdc, TEGRA_GPCDMA_CHAN_CSR),
tdc_read(tdc, TEGRA_GPCDMA_CHAN_STATUS),
tdc_read(tdc, TEGRA_GPCDMA_CHAN_CSRE),
tdc_read(tdc, TEGRA_GPCDMA_CHAN_SRC_PTR),
tdc_read(tdc, TEGRA_GPCDMA_CHAN_DST_PTR)
);
dev_dbg(tdc2dev(tdc), "MCSEQ %x IOSEQ %x WCNT %x XFER %x BSTA %x\n",
tdc_read(tdc, TEGRA_GPCDMA_CHAN_MCSEQ),
tdc_read(tdc, TEGRA_GPCDMA_CHAN_MMIOSEQ),
tdc_read(tdc, TEGRA_GPCDMA_CHAN_WCOUNT),
tdc_read(tdc, TEGRA_GPCDMA_CHAN_XFER_COUNT),
tdc_read(tdc, TEGRA_GPCDMA_CHAN_DMA_BYTE_STATUS)
);
dev_dbg(tdc2dev(tdc), "CSR %x STA %x CSRE %x\n",
tdc_read(tdc, tdc->regs->csr),
tdc_read(tdc, tdc->regs->status),
tdc_read(tdc, tdc->regs->csre));
if (tdc->tdma->chip_data->addr_bits > 39) {
dev_dbg(tdc2dev(tdc), "SRC %x SRC HI %x DST %x DST HI %x\n",
tdc_read(tdc, tdc->regs->src),
tdc_read(tdc, tdc->regs->src_high),
tdc_read(tdc, tdc->regs->dst),
tdc_read(tdc, tdc->regs->dst_high));
} else {
dev_dbg(tdc2dev(tdc), "SRC %x DST %x HI ADDR %x\n",
tdc_read(tdc, tdc->regs->src),
tdc_read(tdc, tdc->regs->dst),
tdc_read(tdc, tdc->regs->high_addr));
}
dev_dbg(tdc2dev(tdc), "MCSEQ %x IOSEQ %x WCNT %x XFER %x WSTA %x\n",
tdc_read(tdc, tdc->regs->mc_seq),
tdc_read(tdc, tdc->regs->mmio_seq),
tdc_read(tdc, tdc->regs->wcount),
tdc_read(tdc, tdc->regs->wxfer),
tdc_read(tdc, tdc->regs->wstatus));
dev_dbg(tdc2dev(tdc), "DMA ERR_STA %x\n",
tdc_read(tdc, TEGRA_GPCDMA_CHAN_ERR_STATUS));
tdc_read(tdc, tdc->regs->err_status));
}
static int tegra_dma_sid_reserve(struct tegra_dma_channel *tdc,
@@ -377,13 +391,13 @@ static int tegra_dma_pause(struct tegra_dma_channel *tdc)
int ret;
u32 val;
val = tdc_read(tdc, TEGRA_GPCDMA_CHAN_CSRE);
val = tdc_read(tdc, tdc->regs->csre);
val |= TEGRA_GPCDMA_CHAN_CSRE_PAUSE;
tdc_write(tdc, TEGRA_GPCDMA_CHAN_CSRE, val);
tdc_write(tdc, tdc->regs->csre, val);
/* Wait until busy bit is de-asserted */
ret = readl_relaxed_poll_timeout_atomic(tdc->tdma->base_addr +
tdc->chan_base_offset + TEGRA_GPCDMA_CHAN_STATUS,
tdc->chan_base_offset + tdc->regs->status,
val,
!(val & TEGRA_GPCDMA_STATUS_BUSY),
TEGRA_GPCDMA_BURST_COMPLETE_TIME,
@@ -419,9 +433,9 @@ static void tegra_dma_resume(struct tegra_dma_channel *tdc)
{
u32 val;
val = tdc_read(tdc, TEGRA_GPCDMA_CHAN_CSRE);
val = tdc_read(tdc, tdc->regs->csre);
val &= ~TEGRA_GPCDMA_CHAN_CSRE_PAUSE;
tdc_write(tdc, TEGRA_GPCDMA_CHAN_CSRE, val);
tdc_write(tdc, tdc->regs->csre, val);
tdc->status = DMA_IN_PROGRESS;
}
@@ -456,27 +470,27 @@ static void tegra_dma_disable(struct tegra_dma_channel *tdc)
{
u32 csr, status;
csr = tdc_read(tdc, TEGRA_GPCDMA_CHAN_CSR);
csr = tdc_read(tdc, tdc->regs->csr);
/* Disable interrupts */
csr &= ~TEGRA_GPCDMA_CSR_IE_EOC;
/* Disable DMA */
csr &= ~TEGRA_GPCDMA_CSR_ENB;
tdc_write(tdc, TEGRA_GPCDMA_CHAN_CSR, csr);
tdc_write(tdc, tdc->regs->csr, csr);
/* Clear interrupt status if it is there */
status = tdc_read(tdc, TEGRA_GPCDMA_CHAN_STATUS);
status = tdc_read(tdc, tdc->regs->status);
if (status & TEGRA_GPCDMA_STATUS_ISE_EOC) {
dev_dbg(tdc2dev(tdc), "%s():clearing interrupt\n", __func__);
tdc_write(tdc, TEGRA_GPCDMA_CHAN_STATUS, status);
tdc_write(tdc, tdc->regs->status, status);
}
}
static void tegra_dma_configure_next_sg(struct tegra_dma_channel *tdc)
{
struct tegra_dma_desc *dma_desc = tdc->dma_desc;
struct tegra_dma_channel_regs *ch_regs;
struct tegra_dma_sg_req *sg_req;
int ret;
u32 val;
@@ -488,29 +502,27 @@ static void tegra_dma_configure_next_sg(struct tegra_dma_channel *tdc)
/* Configure next transfer immediately after DMA is busy */
ret = readl_relaxed_poll_timeout_atomic(tdc->tdma->base_addr +
tdc->chan_base_offset + TEGRA_GPCDMA_CHAN_STATUS,
tdc->chan_base_offset + tdc->regs->status,
val,
(val & TEGRA_GPCDMA_STATUS_BUSY), 0,
TEGRA_GPCDMA_BURST_COMPLETION_TIMEOUT);
if (ret)
return;
ch_regs = &dma_desc->sg_req[dma_desc->sg_idx].ch_regs;
sg_req = &dma_desc->sg_req[dma_desc->sg_idx];
tdc_write(tdc, TEGRA_GPCDMA_CHAN_WCOUNT, ch_regs->wcount);
tdc_write(tdc, TEGRA_GPCDMA_CHAN_SRC_PTR, ch_regs->src_ptr);
tdc_write(tdc, TEGRA_GPCDMA_CHAN_DST_PTR, ch_regs->dst_ptr);
tdc_write(tdc, TEGRA_GPCDMA_CHAN_HIGH_ADDR_PTR, ch_regs->high_addr_ptr);
tdc_write(tdc, tdc->regs->wcount, sg_req->wcount);
tegra_dma_program_addr(tdc, sg_req);
/* Start DMA */
tdc_write(tdc, TEGRA_GPCDMA_CHAN_CSR,
ch_regs->csr | TEGRA_GPCDMA_CSR_ENB);
tdc_write(tdc, tdc->regs->csr,
sg_req->csr | TEGRA_GPCDMA_CSR_ENB);
}
static void tegra_dma_start(struct tegra_dma_channel *tdc)
{
struct tegra_dma_desc *dma_desc = tdc->dma_desc;
struct tegra_dma_channel_regs *ch_regs;
struct tegra_dma_sg_req *sg_req;
struct virt_dma_desc *vdesc;
if (!dma_desc) {
@@ -526,21 +538,19 @@ static void tegra_dma_start(struct tegra_dma_channel *tdc)
tegra_dma_resume(tdc);
}
ch_regs = &dma_desc->sg_req[dma_desc->sg_idx].ch_regs;
sg_req = &dma_desc->sg_req[dma_desc->sg_idx];
tdc_write(tdc, TEGRA_GPCDMA_CHAN_WCOUNT, ch_regs->wcount);
tdc_write(tdc, TEGRA_GPCDMA_CHAN_CSR, 0);
tdc_write(tdc, TEGRA_GPCDMA_CHAN_SRC_PTR, ch_regs->src_ptr);
tdc_write(tdc, TEGRA_GPCDMA_CHAN_DST_PTR, ch_regs->dst_ptr);
tdc_write(tdc, TEGRA_GPCDMA_CHAN_HIGH_ADDR_PTR, ch_regs->high_addr_ptr);
tdc_write(tdc, TEGRA_GPCDMA_CHAN_FIXED_PATTERN, ch_regs->fixed_pattern);
tdc_write(tdc, TEGRA_GPCDMA_CHAN_MMIOSEQ, ch_regs->mmio_seq);
tdc_write(tdc, TEGRA_GPCDMA_CHAN_MCSEQ, ch_regs->mc_seq);
tdc_write(tdc, TEGRA_GPCDMA_CHAN_CSR, ch_regs->csr);
tegra_dma_program_addr(tdc, sg_req);
tdc_write(tdc, tdc->regs->wcount, sg_req->wcount);
tdc_write(tdc, tdc->regs->csr, 0);
tdc_write(tdc, tdc->regs->fixed_pattern, sg_req->fixed_pattern);
tdc_write(tdc, tdc->regs->mmio_seq, sg_req->mmio_seq);
tdc_write(tdc, tdc->regs->mc_seq, sg_req->mc_seq);
tdc_write(tdc, tdc->regs->csr, sg_req->csr);
/* Start DMA */
tdc_write(tdc, TEGRA_GPCDMA_CHAN_CSR,
ch_regs->csr | TEGRA_GPCDMA_CSR_ENB);
tdc_write(tdc, tdc->regs->csr,
sg_req->csr | TEGRA_GPCDMA_CSR_ENB);
}
static void tegra_dma_xfer_complete(struct tegra_dma_channel *tdc)
@@ -601,19 +611,19 @@ static irqreturn_t tegra_dma_isr(int irq, void *dev_id)
u32 status;
/* Check channel error status register */
status = tdc_read(tdc, TEGRA_GPCDMA_CHAN_ERR_STATUS);
status = tdc_read(tdc, tdc->regs->err_status);
if (status) {
tegra_dma_chan_decode_error(tdc, status);
tegra_dma_dump_chan_regs(tdc);
tdc_write(tdc, TEGRA_GPCDMA_CHAN_ERR_STATUS, 0xFFFFFFFF);
tdc_write(tdc, tdc->regs->err_status, 0xFFFFFFFF);
}
spin_lock(&tdc->vc.lock);
status = tdc_read(tdc, TEGRA_GPCDMA_CHAN_STATUS);
status = tdc_read(tdc, tdc->regs->status);
if (!(status & TEGRA_GPCDMA_STATUS_ISE_EOC))
goto irq_done;
tdc_write(tdc, TEGRA_GPCDMA_CHAN_STATUS,
tdc_write(tdc, tdc->regs->status,
TEGRA_GPCDMA_STATUS_ISE_EOC);
if (!dma_desc)
@@ -673,10 +683,10 @@ static int tegra_dma_stop_client(struct tegra_dma_channel *tdc)
* to stop DMA engine from starting any more bursts for
* the given client and wait for in flight bursts to complete
*/
csr = tdc_read(tdc, TEGRA_GPCDMA_CHAN_CSR);
csr = tdc_read(tdc, tdc->regs->csr);
csr &= ~(TEGRA_GPCDMA_CSR_REQ_SEL_MASK);
csr |= TEGRA_GPCDMA_CSR_REQ_SEL_UNUSED;
tdc_write(tdc, TEGRA_GPCDMA_CHAN_CSR, csr);
tdc_write(tdc, tdc->regs->csr, csr);
/* Wait for in flight data transfer to finish */
udelay(TEGRA_GPCDMA_BURST_COMPLETE_TIME);
@@ -687,7 +697,7 @@ static int tegra_dma_stop_client(struct tegra_dma_channel *tdc)
ret = readl_relaxed_poll_timeout_atomic(tdc->tdma->base_addr +
tdc->chan_base_offset +
TEGRA_GPCDMA_CHAN_STATUS,
tdc->regs->status,
status,
!(status & (TEGRA_GPCDMA_STATUS_CHANNEL_TX |
TEGRA_GPCDMA_STATUS_CHANNEL_RX)),
@@ -739,14 +749,14 @@ static int tegra_dma_get_residual(struct tegra_dma_channel *tdc)
unsigned int bytes_xfer, residual;
u32 wcount = 0, status;
wcount = tdc_read(tdc, TEGRA_GPCDMA_CHAN_XFER_COUNT);
wcount = tdc_read(tdc, tdc->regs->wxfer);
/*
* Set wcount = 0 if EOC bit is set. The transfer would have
* already completed and the CHAN_XFER_COUNT could have updated
* for the next transfer, specifically in case of cyclic transfers.
*/
status = tdc_read(tdc, TEGRA_GPCDMA_CHAN_STATUS);
status = tdc_read(tdc, tdc->regs->status);
if (status & TEGRA_GPCDMA_STATUS_ISE_EOC)
wcount = 0;
@@ -825,6 +835,13 @@ static unsigned int get_burst_size(struct tegra_dma_channel *tdc,
* len to calculate the optimum burst size
*/
burst_byte = burst_size ? burst_size * slave_bw : len;
/*
* Find the largest burst size that evenly divides the transfer length.
* The hardware requires the transfer length to be a multiple of the
* burst size - partial bursts are not supported.
*/
burst_byte = min(burst_byte, 1U << __ffs(len));
burst_mmio_width = burst_byte / 4;
if (burst_mmio_width < TEGRA_GPCDMA_MMIOSEQ_BURST_MIN)
@@ -837,7 +854,7 @@ static unsigned int get_burst_size(struct tegra_dma_channel *tdc,
static int get_transfer_param(struct tegra_dma_channel *tdc,
enum dma_transfer_direction direction,
u32 *apb_addr,
dma_addr_t *apb_addr,
u32 *mmio_seq,
u32 *csr,
unsigned int *burst_size,
@@ -893,7 +910,7 @@ tegra_dma_prep_dma_memset(struct dma_chan *dc, dma_addr_t dest, int value,
/* Configure default priority weight for the channel */
csr |= FIELD_PREP(TEGRA_GPCDMA_CSR_WEIGHT, 1);
mc_seq = tdc_read(tdc, TEGRA_GPCDMA_CHAN_MCSEQ);
mc_seq = tdc_read(tdc, tdc->regs->mc_seq);
/* retain stream-id and clean rest */
mc_seq &= TEGRA_GPCDMA_MCSEQ_STREAM_ID0_MASK;
@@ -915,17 +932,15 @@ tegra_dma_prep_dma_memset(struct dma_chan *dc, dma_addr_t dest, int value,
dma_desc->bytes_req = len;
dma_desc->sg_count = 1;
sg_req = dma_desc->sg_req;
sg_req[0].src = 0;
sg_req[0].dst = dest;
sg_req[0].ch_regs.src_ptr = 0;
sg_req[0].ch_regs.dst_ptr = dest;
sg_req[0].ch_regs.high_addr_ptr =
FIELD_PREP(TEGRA_GPCDMA_HIGH_ADDR_DST_PTR, (dest >> 32));
sg_req[0].ch_regs.fixed_pattern = value;
sg_req[0].fixed_pattern = value;
/* Word count reg takes value as (N +1) words */
sg_req[0].ch_regs.wcount = ((len - 4) >> 2);
sg_req[0].ch_regs.csr = csr;
sg_req[0].ch_regs.mmio_seq = 0;
sg_req[0].ch_regs.mc_seq = mc_seq;
sg_req[0].wcount = ((len - 4) >> 2);
sg_req[0].csr = csr;
sg_req[0].mmio_seq = 0;
sg_req[0].mc_seq = mc_seq;
sg_req[0].len = len;
dma_desc->cyclic = false;
@@ -961,7 +976,7 @@ tegra_dma_prep_dma_memcpy(struct dma_chan *dc, dma_addr_t dest,
/* Configure default priority weight for the channel */
csr |= FIELD_PREP(TEGRA_GPCDMA_CSR_WEIGHT, 1);
mc_seq = tdc_read(tdc, TEGRA_GPCDMA_CHAN_MCSEQ);
mc_seq = tdc_read(tdc, tdc->regs->mc_seq);
/* retain stream-id and clean rest */
mc_seq &= (TEGRA_GPCDMA_MCSEQ_STREAM_ID0_MASK) |
(TEGRA_GPCDMA_MCSEQ_STREAM_ID1_MASK);
@@ -985,17 +1000,14 @@ tegra_dma_prep_dma_memcpy(struct dma_chan *dc, dma_addr_t dest,
dma_desc->sg_count = 1;
sg_req = dma_desc->sg_req;
sg_req[0].ch_regs.src_ptr = src;
sg_req[0].ch_regs.dst_ptr = dest;
sg_req[0].ch_regs.high_addr_ptr =
FIELD_PREP(TEGRA_GPCDMA_HIGH_ADDR_SRC_PTR, (src >> 32));
sg_req[0].ch_regs.high_addr_ptr |=
FIELD_PREP(TEGRA_GPCDMA_HIGH_ADDR_DST_PTR, (dest >> 32));
sg_req[0].src = src;
sg_req[0].dst = dest;
/* Word count reg takes value as (N +1) words */
sg_req[0].ch_regs.wcount = ((len - 4) >> 2);
sg_req[0].ch_regs.csr = csr;
sg_req[0].ch_regs.mmio_seq = 0;
sg_req[0].ch_regs.mc_seq = mc_seq;
sg_req[0].wcount = ((len - 4) >> 2);
sg_req[0].csr = csr;
sg_req[0].mmio_seq = 0;
sg_req[0].mc_seq = mc_seq;
sg_req[0].len = len;
dma_desc->cyclic = false;
@@ -1010,7 +1022,8 @@ tegra_dma_prep_slave_sg(struct dma_chan *dc, struct scatterlist *sgl,
struct tegra_dma_channel *tdc = to_tegra_dma_chan(dc);
unsigned int max_dma_count = tdc->tdma->chip_data->max_dma_count;
enum dma_slave_buswidth slave_bw = DMA_SLAVE_BUSWIDTH_UNDEFINED;
u32 csr, mc_seq, apb_ptr = 0, mmio_seq = 0;
u32 csr, mc_seq, mmio_seq = 0;
dma_addr_t apb_ptr = 0;
struct tegra_dma_sg_req *sg_req;
struct tegra_dma_desc *dma_desc;
struct scatterlist *sg;
@@ -1049,7 +1062,7 @@ tegra_dma_prep_slave_sg(struct dma_chan *dc, struct scatterlist *sgl,
if (flags & DMA_PREP_INTERRUPT)
csr |= TEGRA_GPCDMA_CSR_IE_EOC;
mc_seq = tdc_read(tdc, TEGRA_GPCDMA_CHAN_MCSEQ);
mc_seq = tdc_read(tdc, tdc->regs->mc_seq);
/* retain stream-id and clean rest */
mc_seq &= TEGRA_GPCDMA_MCSEQ_STREAM_ID0_MASK;
@@ -1096,25 +1109,21 @@ tegra_dma_prep_slave_sg(struct dma_chan *dc, struct scatterlist *sgl,
dma_desc->bytes_req += len;
if (direction == DMA_MEM_TO_DEV) {
sg_req[i].ch_regs.src_ptr = mem;
sg_req[i].ch_regs.dst_ptr = apb_ptr;
sg_req[i].ch_regs.high_addr_ptr =
FIELD_PREP(TEGRA_GPCDMA_HIGH_ADDR_SRC_PTR, (mem >> 32));
sg_req[i].src = mem;
sg_req[i].dst = apb_ptr;
} else if (direction == DMA_DEV_TO_MEM) {
sg_req[i].ch_regs.src_ptr = apb_ptr;
sg_req[i].ch_regs.dst_ptr = mem;
sg_req[i].ch_regs.high_addr_ptr =
FIELD_PREP(TEGRA_GPCDMA_HIGH_ADDR_DST_PTR, (mem >> 32));
sg_req[i].src = apb_ptr;
sg_req[i].dst = mem;
}
/*
* Word count register takes input in words. Writing a value
* of N into word count register means a req of (N+1) words.
*/
sg_req[i].ch_regs.wcount = ((len - 4) >> 2);
sg_req[i].ch_regs.csr = csr;
sg_req[i].ch_regs.mmio_seq = mmio_seq;
sg_req[i].ch_regs.mc_seq = mc_seq;
sg_req[i].wcount = ((len - 4) >> 2);
sg_req[i].csr = csr;
sg_req[i].mmio_seq = mmio_seq;
sg_req[i].mc_seq = mc_seq;
sg_req[i].len = len;
}
@@ -1128,7 +1137,8 @@ tegra_dma_prep_dma_cyclic(struct dma_chan *dc, dma_addr_t buf_addr, size_t buf_l
unsigned long flags)
{
enum dma_slave_buswidth slave_bw = DMA_SLAVE_BUSWIDTH_UNDEFINED;
u32 csr, mc_seq, apb_ptr = 0, mmio_seq = 0, burst_size;
u32 csr, mc_seq, mmio_seq = 0, burst_size;
dma_addr_t apb_ptr = 0;
unsigned int max_dma_count, len, period_count, i;
struct tegra_dma_channel *tdc = to_tegra_dma_chan(dc);
struct tegra_dma_desc *dma_desc;
@@ -1186,7 +1196,7 @@ tegra_dma_prep_dma_cyclic(struct dma_chan *dc, dma_addr_t buf_addr, size_t buf_l
mmio_seq |= FIELD_PREP(TEGRA_GPCDMA_MMIOSEQ_WRAP_WORD, 1);
mc_seq = tdc_read(tdc, TEGRA_GPCDMA_CHAN_MCSEQ);
mc_seq = tdc_read(tdc, tdc->regs->mc_seq);
/* retain stream-id and clean rest */
mc_seq &= TEGRA_GPCDMA_MCSEQ_STREAM_ID0_MASK;
@@ -1217,24 +1227,20 @@ tegra_dma_prep_dma_cyclic(struct dma_chan *dc, dma_addr_t buf_addr, size_t buf_l
for (i = 0; i < period_count; i++) {
mmio_seq |= get_burst_size(tdc, burst_size, slave_bw, len);
if (direction == DMA_MEM_TO_DEV) {
sg_req[i].ch_regs.src_ptr = mem;
sg_req[i].ch_regs.dst_ptr = apb_ptr;
sg_req[i].ch_regs.high_addr_ptr =
FIELD_PREP(TEGRA_GPCDMA_HIGH_ADDR_SRC_PTR, (mem >> 32));
sg_req[i].src = mem;
sg_req[i].dst = apb_ptr;
} else if (direction == DMA_DEV_TO_MEM) {
sg_req[i].ch_regs.src_ptr = apb_ptr;
sg_req[i].ch_regs.dst_ptr = mem;
sg_req[i].ch_regs.high_addr_ptr =
FIELD_PREP(TEGRA_GPCDMA_HIGH_ADDR_DST_PTR, (mem >> 32));
sg_req[i].src = apb_ptr;
sg_req[i].dst = mem;
}
/*
* Word count register takes input in words. Writing a value
* of N into word count register means a req of (N+1) words.
*/
sg_req[i].ch_regs.wcount = ((len - 4) >> 2);
sg_req[i].ch_regs.csr = csr;
sg_req[i].ch_regs.mmio_seq = mmio_seq;
sg_req[i].ch_regs.mc_seq = mc_seq;
sg_req[i].wcount = ((len - 4) >> 2);
sg_req[i].csr = csr;
sg_req[i].mmio_seq = mmio_seq;
sg_req[i].mc_seq = mc_seq;
sg_req[i].len = len;
mem += len;
@@ -1304,27 +1310,76 @@ static struct dma_chan *tegra_dma_of_xlate(struct of_phandle_args *dma_spec,
return chan;
}
static const struct tegra_dma_channel_regs tegra186_reg_offsets = {
.csr = 0x0,
.status = 0x4,
.csre = 0x8,
.src = 0xc,
.dst = 0x10,
.high_addr = 0x14,
.mc_seq = 0x18,
.mmio_seq = 0x1c,
.wcount = 0x20,
.wxfer = 0x24,
.wstatus = 0x28,
.err_status = 0x30,
.fixed_pattern = 0x34,
};
static const struct tegra_dma_channel_regs tegra264_reg_offsets = {
.csr = 0x0,
.status = 0x4,
.csre = 0x8,
.src = 0xc,
.dst = 0x10,
.src_high = 0x14,
.dst_high = 0x18,
.mc_seq = 0x1c,
.mmio_seq = 0x20,
.wcount = 0x24,
.wxfer = 0x28,
.wstatus = 0x2c,
.err_status = 0x34,
.fixed_pattern = 0x38,
};
static const struct tegra_dma_chip_data tegra186_dma_chip_data = {
.nr_channels = 32,
.addr_bits = 39,
.channel_reg_size = SZ_64K,
.max_dma_count = SZ_1G,
.hw_support_pause = false,
.channel_regs = &tegra186_reg_offsets,
.terminate = tegra_dma_stop_client,
};
static const struct tegra_dma_chip_data tegra194_dma_chip_data = {
.nr_channels = 32,
.addr_bits = 39,
.channel_reg_size = SZ_64K,
.max_dma_count = SZ_1G,
.hw_support_pause = true,
.channel_regs = &tegra186_reg_offsets,
.terminate = tegra_dma_pause,
};
static const struct tegra_dma_chip_data tegra234_dma_chip_data = {
.nr_channels = 32,
.addr_bits = 39,
.channel_reg_size = SZ_64K,
.max_dma_count = SZ_1G,
.hw_support_pause = true,
.channel_regs = &tegra186_reg_offsets,
.terminate = tegra_dma_pause_noerr,
};
static const struct tegra_dma_chip_data tegra264_dma_chip_data = {
.nr_channels = 32,
.addr_bits = 41,
.channel_reg_size = SZ_64K,
.max_dma_count = SZ_1G,
.hw_support_pause = true,
.channel_regs = &tegra264_reg_offsets,
.terminate = tegra_dma_pause_noerr,
};
@@ -1338,6 +1393,9 @@ static const struct of_device_id tegra_dma_of_match[] = {
}, {
.compatible = "nvidia,tegra234-gpcdma",
.data = &tegra234_dma_chip_data,
}, {
.compatible = "nvidia,tegra264-gpcdma",
.data = &tegra264_dma_chip_data,
}, {
},
};
@@ -1345,7 +1403,7 @@ MODULE_DEVICE_TABLE(of, tegra_dma_of_match);
static int tegra_dma_program_sid(struct tegra_dma_channel *tdc, int stream_id)
{
unsigned int reg_val = tdc_read(tdc, TEGRA_GPCDMA_CHAN_MCSEQ);
unsigned int reg_val = tdc_read(tdc, tdc->regs->mc_seq);
reg_val &= ~(TEGRA_GPCDMA_MCSEQ_STREAM_ID0_MASK);
reg_val &= ~(TEGRA_GPCDMA_MCSEQ_STREAM_ID1_MASK);
@@ -1353,16 +1411,20 @@ static int tegra_dma_program_sid(struct tegra_dma_channel *tdc, int stream_id)
reg_val |= FIELD_PREP(TEGRA_GPCDMA_MCSEQ_STREAM_ID0_MASK, stream_id);
reg_val |= FIELD_PREP(TEGRA_GPCDMA_MCSEQ_STREAM_ID1_MASK, stream_id);
tdc_write(tdc, TEGRA_GPCDMA_CHAN_MCSEQ, reg_val);
tdc_write(tdc, tdc->regs->mc_seq, reg_val);
return 0;
}
static int tegra_dma_probe(struct platform_device *pdev)
{
const struct tegra_dma_chip_data *cdata = NULL;
struct tegra_dma_channel *tdc;
struct tegra_dma *tdma;
struct dma_chan *chan;
struct device *chdev;
bool use_iommu_map = false;
unsigned int i;
u32 stream_id;
struct tegra_dma *tdma;
int ret;
cdata = of_device_get_match_data(&pdev->dev);
@@ -1381,18 +1443,19 @@ static int tegra_dma_probe(struct platform_device *pdev)
if (IS_ERR(tdma->base_addr))
return PTR_ERR(tdma->base_addr);
tdma->rst = devm_reset_control_get_exclusive(&pdev->dev, "gpcdma");
tdma->rst = devm_reset_control_get_optional_exclusive(&pdev->dev, "gpcdma");
if (IS_ERR(tdma->rst)) {
return dev_err_probe(&pdev->dev, PTR_ERR(tdma->rst),
"Missing controller reset\n");
"Failed to get controller reset\n");
}
reset_control_reset(tdma->rst);
tdma->dma_dev.dev = &pdev->dev;
if (!tegra_dev_iommu_get_stream_id(&pdev->dev, &stream_id)) {
dev_err(&pdev->dev, "Missing iommu stream-id\n");
return -EINVAL;
use_iommu_map = of_property_present(pdev->dev.of_node, "iommu-map");
if (!use_iommu_map) {
if (!tegra_dev_iommu_get_stream_id(&pdev->dev, &stream_id))
return dev_err_probe(&pdev->dev, -EINVAL, "Missing iommu stream-id\n");
}
ret = device_property_read_u32(&pdev->dev, "dma-channel-mask",
@@ -1404,9 +1467,10 @@ static int tegra_dma_probe(struct platform_device *pdev)
tdma->chan_mask = TEGRA_GPCDMA_DEFAULT_CHANNEL_MASK;
}
/* Initialize vchan for each channel and populate the channels list */
INIT_LIST_HEAD(&tdma->dma_dev.channels);
for (i = 0; i < cdata->nr_channels; i++) {
struct tegra_dma_channel *tdc = &tdma->channels[i];
tdc = &tdma->channels[i];
/* Check for channel mask */
if (!(tdma->chan_mask & BIT(i)))
@@ -1419,18 +1483,17 @@ static int tegra_dma_probe(struct platform_device *pdev)
tdc->chan_base_offset = TEGRA_GPCDMA_CHANNEL_BASE_ADDR_OFFSET +
i * cdata->channel_reg_size;
snprintf(tdc->name, sizeof(tdc->name), "gpcdma.%d", i);
tdc->regs = cdata->channel_regs;
tdc->tdma = tdma;
tdc->id = i;
tdc->slave_id = -1;
vchan_init(&tdc->vc, &tdma->dma_dev);
tdc->vc.desc_free = tegra_dma_desc_free;
/* program stream-id for this channel */
tegra_dma_program_sid(tdc, stream_id);
tdc->stream_id = stream_id;
}
dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(cdata->addr_bits));
dma_cap_set(DMA_SLAVE, tdma->dma_dev.cap_mask);
dma_cap_set(DMA_PRIVATE, tdma->dma_dev.cap_mask);
dma_cap_set(DMA_MEMCPY, tdma->dma_dev.cap_mask);
@@ -1460,37 +1523,59 @@ static int tegra_dma_probe(struct platform_device *pdev)
tdma->dma_dev.device_synchronize = tegra_dma_chan_synchronize;
tdma->dma_dev.residue_granularity = DMA_RESIDUE_GRANULARITY_BURST;
ret = dma_async_device_register(&tdma->dma_dev);
/* Register the DMA device and the channels */
ret = dmaenginem_async_device_register(&tdma->dma_dev);
if (ret < 0) {
dev_err_probe(&pdev->dev, ret,
"GPC DMA driver registration failed\n");
return ret;
}
ret = of_dma_controller_register(pdev->dev.of_node,
tegra_dma_of_xlate, tdma);
/*
* Configure stream ID for each channel from the channels registered
* above. This is done in a separate iteration to ensure that only
* the channels available and registered for the DMA device are used.
*/
list_for_each_entry(chan, &tdma->dma_dev.channels, device_node) {
chdev = &chan->dev->device;
tdc = to_tegra_dma_chan(chan);
if (use_iommu_map) {
chdev->bus = pdev->dev.bus;
dma_coerce_mask_and_coherent(chdev, DMA_BIT_MASK(cdata->addr_bits));
ret = of_dma_configure_id(chdev, pdev->dev.of_node,
true, &tdc->id);
if (ret)
return dev_err_probe(chdev, ret,
"Failed to configure IOMMU for channel %d\n", tdc->id);
if (!tegra_dev_iommu_get_stream_id(chdev, &stream_id))
return dev_err_probe(chdev, -EINVAL,
"Failed to get stream ID for channel %d\n", tdc->id);
chan->dev->chan_dma_dev = true;
}
/* program stream-id for this channel */
tegra_dma_program_sid(tdc, stream_id);
tdc->stream_id = stream_id;
}
ret = devm_of_dma_controller_register(&pdev->dev, pdev->dev.of_node,
tegra_dma_of_xlate, tdma);
if (ret < 0) {
dev_err_probe(&pdev->dev, ret,
"GPC DMA OF registration failed\n");
dma_async_device_unregister(&tdma->dma_dev);
return ret;
}
dev_info(&pdev->dev, "GPC DMA driver register %lu channels\n",
dev_info(&pdev->dev, "GPC DMA driver registered %lu channels\n",
hweight_long(tdma->chan_mask));
return 0;
}
static void tegra_dma_remove(struct platform_device *pdev)
{
struct tegra_dma *tdma = platform_get_drvdata(pdev);
of_dma_controller_free(pdev->dev.of_node);
dma_async_device_unregister(&tdma->dma_dev);
}
static int __maybe_unused tegra_dma_pm_suspend(struct device *dev)
{
struct tegra_dma *tdma = dev_get_drvdata(dev);
@@ -1541,7 +1626,6 @@ static struct platform_driver tegra_dma_driver = {
.of_match_table = tegra_dma_of_match,
},
.probe = tegra_dma_probe,
.remove = tegra_dma_remove,
};
module_platform_driver(tegra_dma_driver);

View File

@@ -335,8 +335,16 @@ static int tegra_adma_request_alloc(struct tegra_adma_chan *tdc,
struct tegra_adma *tdma = tdc->tdma;
unsigned int sreq_index = tdc->sreq_index;
if (tdc->sreq_reserved)
return tdc->sreq_dir == direction ? 0 : -EINVAL;
if (tdc->sreq_reserved) {
if (tdc->sreq_dir != direction) {
dev_err(tdma->dev,
"DMA request direction mismatch: reserved=%s, requested=%s\n",
dmaengine_get_direction_text(tdc->sreq_dir),
dmaengine_get_direction_text(direction));
return -EINVAL;
}
return 0;
}
if (sreq_index > tdma->cdata->ch_req_max) {
dev_err(tdma->dev, "invalid DMA request\n");
@@ -665,8 +673,11 @@ static int tegra_adma_set_xfer_params(struct tegra_adma_chan *tdc,
const struct tegra_adma_chip_data *cdata = tdc->tdma->cdata;
unsigned int burst_size, adma_dir, fifo_size_shift;
if (desc->num_periods > ADMA_CH_CONFIG_MAX_BUFS)
if (desc->num_periods > ADMA_CH_CONFIG_MAX_BUFS) {
dev_err(tdc2dev(tdc), "invalid DMA periods %zu (max %u)\n",
desc->num_periods, ADMA_CH_CONFIG_MAX_BUFS);
return -EINVAL;
}
switch (direction) {
case DMA_MEM_TO_DEV:
@@ -1029,8 +1040,8 @@ static int tegra_adma_probe(struct platform_device *pdev)
cdata = of_device_get_match_data(&pdev->dev);
if (!cdata) {
dev_err(&pdev->dev, "device match data not found\n");
return -ENODEV;
return dev_err_probe(&pdev->dev, -ENODEV,
"device match data not found\n");
}
tdma = devm_kzalloc(&pdev->dev,
@@ -1056,7 +1067,8 @@ static int tegra_adma_probe(struct platform_device *pdev)
unsigned int ch_base_offset;
if (res_page->start < res_base->start)
return -EINVAL;
return dev_err_probe(&pdev->dev, -EINVAL,
"invalid page/global resource order\n");
page_offset = res_page->start - res_base->start;
ch_base_offset = cdata->ch_base_offset;
if (!ch_base_offset)
@@ -1064,7 +1076,9 @@ static int tegra_adma_probe(struct platform_device *pdev)
page_no = div_u64(page_offset, ch_base_offset);
if (!page_no || page_no > INT_MAX)
return -EINVAL;
return dev_err_probe(&pdev->dev, -EINVAL,
"invalid page number %llu\n",
(unsigned long long)page_no);
tdma->ch_page_no = page_no - 1;
tdma->base_addr = devm_ioremap_resource(&pdev->dev, res_base);
@@ -1079,7 +1093,8 @@ static int tegra_adma_probe(struct platform_device *pdev)
if (IS_ERR(tdma->base_addr))
return PTR_ERR(tdma->base_addr);
} else {
return -ENODEV;
return dev_err_probe(&pdev->dev, -ENODEV,
"failed to get memory resource\n");
}
tdma->ch_base_addr = tdma->base_addr + cdata->ch_base_offset;
@@ -1087,8 +1102,8 @@ static int tegra_adma_probe(struct platform_device *pdev)
tdma->ahub_clk = devm_clk_get(&pdev->dev, "d_audio");
if (IS_ERR(tdma->ahub_clk)) {
dev_err(&pdev->dev, "Error: Missing ahub controller clock\n");
return PTR_ERR(tdma->ahub_clk);
return dev_err_probe(&pdev->dev, PTR_ERR(tdma->ahub_clk),
"failed to get ahub clock\n");
}
tdma->dma_chan_mask = devm_kzalloc(&pdev->dev,
@@ -1104,8 +1119,8 @@ static int tegra_adma_probe(struct platform_device *pdev)
(u32 *)tdma->dma_chan_mask,
BITS_TO_U32(tdma->nr_channels));
if (ret < 0 && (ret != -EINVAL)) {
dev_err(&pdev->dev, "dma-channel-mask is not complete.\n");
return ret;
return dev_err_probe(&pdev->dev, ret,
"dma-channel-mask is not complete.\n");
}
INIT_LIST_HEAD(&tdma->dma_dev.channels);
@@ -1127,11 +1142,13 @@ static int tegra_adma_probe(struct platform_device *pdev)
cdata->global_ch_config_base + (4 * i);
}
tdc->irq = of_irq_get(pdev->dev.of_node, i);
if (tdc->irq <= 0) {
ret = tdc->irq ?: -ENXIO;
ret = of_irq_get(pdev->dev.of_node, i);
if (ret <= 0) {
ret = dev_err_probe(&pdev->dev, ret ?: -ENXIO,
"failed to get IRQ for channel %d\n", i);
goto irq_dispose;
}
tdc->irq = ret;
vchan_init(&tdc->vc, &tdma->dma_dev);
tdc->vc.desc_free = tegra_adma_desc_free;
@@ -1141,12 +1158,18 @@ static int tegra_adma_probe(struct platform_device *pdev)
pm_runtime_enable(&pdev->dev);
ret = pm_runtime_resume_and_get(&pdev->dev);
if (ret < 0)
if (ret < 0) {
ret = dev_err_probe(&pdev->dev, ret,
"runtime PM resume failed\n");
goto rpm_disable;
}
ret = tegra_adma_init(tdma);
if (ret)
if (ret) {
ret = dev_err_probe(&pdev->dev, ret,
"failed to initialize ADMA\n");
goto rpm_put;
}
dma_cap_set(DMA_SLAVE, tdma->dma_dev.cap_mask);
dma_cap_set(DMA_PRIVATE, tdma->dma_dev.cap_mask);
@@ -1172,14 +1195,16 @@ static int tegra_adma_probe(struct platform_device *pdev)
ret = dma_async_device_register(&tdma->dma_dev);
if (ret < 0) {
dev_err(&pdev->dev, "ADMA registration failed: %d\n", ret);
ret = dev_err_probe(&pdev->dev, ret,
"ADMA registration failed\n");
goto rpm_put;
}
ret = of_dma_controller_register(pdev->dev.of_node,
tegra_dma_of_xlate, tdma);
if (ret < 0) {
dev_err(&pdev->dev, "ADMA OF registration failed %d\n", ret);
ret = dev_err_probe(&pdev->dev, ret,
"ADMA OF registration failed\n");
goto dma_remove;
}

View File

@@ -151,6 +151,12 @@ struct rzv2h_hw_info {
#define ICU_DMAC_PREP_DMAREQ(sel, up) (FIELD_PREP(ICU_DMAC_DkRQ_SEL_MASK, (sel)) \
<< ICU_DMAC_DMAREQ_SHIFT(up))
/* DMAC ACK routing - 4 x 7-bit fields per 32-bit register, 8-bit spacing */
#define ICU_DMAC_DACK_SEL_MASK GENMASK(6, 0)
#define ICU_DMAC_DACK_SHIFT(n) ((n) * 8)
#define ICU_DMAC_DACK_FIELD_MASK(n) (ICU_DMAC_DACK_SEL_MASK << ICU_DMAC_DACK_SHIFT(n))
#define ICU_DMAC_PREP_DACK(val, n) (((val) & ICU_DMAC_DACK_SEL_MASK) << ICU_DMAC_DACK_SHIFT(n))
/**
* struct rzv2h_icu_priv - Interrupt Control Unit controller private data structure.
* @base: Controller's base address
@@ -188,6 +194,40 @@ void rzv2h_icu_register_dma_req(struct platform_device *icu_dev, u8 dmac_index,
}
EXPORT_SYMBOL_GPL(rzv2h_icu_register_dma_req);
/**
* rzv2h_icu_register_dma_ack - Configure DMA ACK signal routing
* @icu_dev: ICU platform device
* @dmac_index: DMAC instance index (0-4)
* @dmac_channel: DMAC channel number (0-15), or RZV2H_ICU_DMAC_ACK_NO_DEFAULT
* to disconnect routing for a given ack_no
* @ack_no: Peripheral ACK number (0-88) per RZ/G3E manual Table 4.6-28,
* used as index into ICU_DMACKSELk
*
* Routes the ACK signal of the peripheral identified by @ack_no to DMAC
* channel @dmac_channel of instance @dmac_index. When @dmac_channel is
* RZV2H_ICU_DMAC_ACK_NO_DEFAULT the field is reset, disconnecting any
* previously configured routing for that peripheral.
*/
void rzv2h_icu_register_dma_ack(struct platform_device *icu_dev, u8 dmac_index,
u8 dmac_channel, u16 ack_no)
{
struct rzv2h_icu_priv *priv = platform_get_drvdata(icu_dev);
u8 reg_idx = ack_no / 4;
u8 field_idx = ack_no & 0x3;
u8 dmac_ack_src = (dmac_channel == RZV2H_ICU_DMAC_ACK_NO_DEFAULT) ?
RZV2H_ICU_DMAC_ACK_NO_DEFAULT :
(dmac_index * 16 + dmac_channel);
u32 val;
guard(raw_spinlock_irqsave)(&priv->lock);
val = readl(priv->base + ICU_DMACKSELk(reg_idx));
val &= ~ICU_DMAC_DACK_FIELD_MASK(field_idx);
val |= ICU_DMAC_PREP_DACK(dmac_ack_src, field_idx);
writel(val, priv->base + ICU_DMACKSELk(reg_idx));
}
EXPORT_SYMBOL_GPL(rzv2h_icu_register_dma_ack);
static inline struct rzv2h_icu_priv *irq_data_to_priv(struct irq_data *data)
{
return data->domain->host_data;

View File

@@ -11,13 +11,18 @@
#include <linux/platform_device.h>
#define RZV2H_ICU_DMAC_REQ_NO_DEFAULT 0x3ff
#define RZV2H_ICU_DMAC_ACK_NO_DEFAULT 0x7f
#ifdef CONFIG_RENESAS_RZV2H_ICU
void rzv2h_icu_register_dma_req(struct platform_device *icu_dev, u8 dmac_index, u8 dmac_channel,
u16 req_no);
void rzv2h_icu_register_dma_ack(struct platform_device *icu_dev, u8 dmac_index,
u8 dmac_channel, u16 ack_no);
#else
static inline void rzv2h_icu_register_dma_req(struct platform_device *icu_dev, u8 dmac_index,
u8 dmac_channel, u16 req_no) { }
static inline void rzv2h_icu_register_dma_ack(struct platform_device *icu_dev, u8 dmac_index,
u8 dmac_channel, u16 ack_no) { }
#endif
#endif /* __LINUX_IRQ_RENESAS_RZV2H */

View File

@@ -1,110 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright © 2006, Intel Corporation.
*/
#ifndef IOP_ADMA_H
#define IOP_ADMA_H
#include <linux/types.h>
#include <linux/dmaengine.h>
#include <linux/interrupt.h>
#define IOP_ADMA_SLOT_SIZE 32
#define IOP_ADMA_THRESHOLD 4
#ifdef DEBUG
#define IOP_PARANOIA 1
#else
#define IOP_PARANOIA 0
#endif
#define iop_paranoia(x) BUG_ON(IOP_PARANOIA && (x))
#define DMA0_ID 0
#define DMA1_ID 1
#define AAU_ID 2
/**
* struct iop_adma_device - internal representation of an ADMA device
* @pdev: Platform device
* @id: HW ADMA Device selector
* @dma_desc_pool: base of DMA descriptor region (DMA address)
* @dma_desc_pool_virt: base of DMA descriptor region (CPU address)
* @common: embedded struct dma_device
*/
struct iop_adma_device {
struct platform_device *pdev;
int id;
dma_addr_t dma_desc_pool;
void *dma_desc_pool_virt;
struct dma_device common;
};
/**
* struct iop_adma_chan - internal representation of an ADMA device
* @pending: allows batching of hardware operations
* @lock: serializes enqueue/dequeue operations to the slot pool
* @mmr_base: memory mapped register base
* @chain: device chain view of the descriptors
* @device: parent device
* @common: common dmaengine channel object members
* @last_used: place holder for allocation to continue from where it left off
* @all_slots: complete domain of slots usable by the channel
* @slots_allocated: records the actual size of the descriptor slot pool
* @irq_tasklet: bottom half where iop_adma_slot_cleanup runs
*/
struct iop_adma_chan {
int pending;
spinlock_t lock; /* protects the descriptor slot pool */
void __iomem *mmr_base;
struct list_head chain;
struct iop_adma_device *device;
struct dma_chan common;
struct iop_adma_desc_slot *last_used;
struct list_head all_slots;
int slots_allocated;
struct tasklet_struct irq_tasklet;
};
/**
* struct iop_adma_desc_slot - IOP-ADMA software descriptor
* @slot_node: node on the iop_adma_chan.all_slots list
* @chain_node: node on the op_adma_chan.chain list
* @hw_desc: virtual address of the hardware descriptor chain
* @phys: hardware address of the hardware descriptor chain
* @group_head: first operation in a transaction
* @slot_cnt: total slots used in an transaction (group of operations)
* @slots_per_op: number of slots per operation
* @idx: pool index
* @tx_list: list of descriptors that are associated with one operation
* @async_tx: support for the async_tx api
* @group_list: list of slots that make up a multi-descriptor transaction
* for example transfer lengths larger than the supported hw max
* @xor_check_result: result of zero sum
* @crc32_result: result crc calculation
*/
struct iop_adma_desc_slot {
struct list_head slot_node;
struct list_head chain_node;
void *hw_desc;
struct iop_adma_desc_slot *group_head;
u16 slot_cnt;
u16 slots_per_op;
u16 idx;
struct list_head tx_list;
struct dma_async_tx_descriptor async_tx;
union {
u32 *xor_check_result;
u32 *crc32_result;
u32 *pq_check_result;
};
};
struct iop_adma_platform_data {
int hw_id;
dma_cap_mask_t cap_mask;
size_t pool_size;
};
#define to_iop_sw_desc(addr_hw_desc) \
container_of(addr_hw_desc, struct iop_adma_desc_slot, hw_desc)
#define iop_hw_desc_slot_idx(hw_desc, idx) \
( (void *) (((unsigned long) hw_desc) + ((idx) << 5)) )
#endif

View File

@@ -56,6 +56,7 @@ config SND_SOC_MSIOF
config SND_SOC_RZ
tristate "RZ/G2L series SSIF-2 support"
depends on ARCH_RZG2L || COMPILE_TEST
select SND_SOC_GENERIC_DMAENGINE_PCM
help
This option enables RZ/G2L SSIF-2 sound support.

View File

@@ -13,6 +13,8 @@
#include <linux/module.h>
#include <linux/pm_runtime.h>
#include <linux/reset.h>
#include <sound/dmaengine_pcm.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
@@ -87,8 +89,6 @@ struct rz_ssi_stream {
struct rz_ssi_priv *priv;
struct snd_pcm_substream *substream;
int fifo_sample_size; /* sample capacity of SSI FIFO */
int dma_buffer_pos; /* The address for the next DMA descriptor */
int completed_dma_buf_pos; /* The address of the last completed DMA descriptor. */
int period_counter; /* for keeping track of periods transferred */
int buffer_pos; /* current frame position in the buffer */
int running; /* 0=stopped, 1=running */
@@ -96,8 +96,6 @@ struct rz_ssi_stream {
int uerr_num;
int oerr_num;
struct dma_chan *dma_ch;
int (*transfer)(struct rz_ssi_priv *ssi, struct rz_ssi_stream *strm);
};
@@ -108,7 +106,6 @@ struct rz_ssi_priv {
struct clk *sfr_clk;
struct clk *clk;
phys_addr_t phys;
int irq_int;
int irq_tx;
int irq_rx;
@@ -148,9 +145,10 @@ struct rz_ssi_priv {
unsigned int sample_width;
unsigned int sample_bits;
} hw_params_cache;
};
static void rz_ssi_dma_complete(void *data);
struct snd_dmaengine_dai_dma_data dma_dais[SNDRV_PCM_STREAM_LAST + 1];
struct dma_chan *dmas[SNDRV_PCM_STREAM_LAST + 1];
};
static void rz_ssi_reg_writel(struct rz_ssi_priv *priv, uint reg, u32 data)
{
@@ -172,11 +170,6 @@ static void rz_ssi_reg_mask_setl(struct rz_ssi_priv *priv, uint reg,
writel(val, (priv->base + reg));
}
static inline bool rz_ssi_stream_is_play(struct snd_pcm_substream *substream)
{
return substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
}
static inline struct rz_ssi_stream *
rz_ssi_stream_get(struct rz_ssi_priv *ssi, struct snd_pcm_substream *substream)
{
@@ -185,7 +178,7 @@ rz_ssi_stream_get(struct rz_ssi_priv *ssi, struct snd_pcm_substream *substream)
static inline bool rz_ssi_is_dma_enabled(struct rz_ssi_priv *ssi)
{
return (ssi->playback.dma_ch && (ssi->dma_rt || ssi->capture.dma_ch));
return !ssi->playback.transfer && !ssi->capture.transfer;
}
static void rz_ssi_set_substream(struct rz_ssi_stream *strm,
@@ -215,8 +208,6 @@ static void rz_ssi_stream_init(struct rz_ssi_stream *strm,
struct snd_pcm_substream *substream)
{
rz_ssi_set_substream(strm, substream);
strm->dma_buffer_pos = 0;
strm->completed_dma_buf_pos = 0;
strm->period_counter = 0;
strm->buffer_pos = 0;
@@ -242,12 +233,13 @@ static void rz_ssi_stream_quit(struct rz_ssi_priv *ssi,
dev_info(dev, "underrun = %d\n", strm->uerr_num);
}
static int rz_ssi_clk_setup(struct rz_ssi_priv *ssi, unsigned int rate,
unsigned int channels)
static int rz_ssi_clk_setup(struct rz_ssi_priv *ssi, struct snd_pcm_substream *substream,
unsigned int rate, unsigned int channels)
{
static u8 ckdv[] = { 1, 2, 4, 8, 16, 32, 64, 128, 6, 12, 24, 48, 96 };
unsigned int channel_bits = 32; /* System Word Length */
unsigned long bclk_rate = rate * channels * channel_bits;
struct snd_dmaengine_dai_dma_data *dma_dai;
unsigned int div;
unsigned int i;
u32 ssicr = 0;
@@ -290,6 +282,8 @@ static int rz_ssi_clk_setup(struct rz_ssi_priv *ssi, unsigned int rate,
return -EINVAL;
}
dma_dai = &ssi->dma_dais[substream->stream];
/*
* DWL: Data Word Length = {16, 24, 32} bits
* SWL: System Word Length = 32 bits
@@ -298,12 +292,15 @@ static int rz_ssi_clk_setup(struct rz_ssi_priv *ssi, unsigned int rate,
switch (ssi->hw_params_cache.sample_width) {
case 16:
ssicr |= SSICR_DWL(1);
dma_dai->addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
break;
case 24:
ssicr |= SSICR_DWL(5) | SSICR_PDTA;
dma_dai->addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
break;
case 32:
ssicr |= SSICR_DWL(6);
dma_dai->addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
break;
default:
dev_err(ssi->dev, "Not support %u data width",
@@ -344,7 +341,7 @@ static void rz_ssi_set_idle(struct rz_ssi_priv *ssi)
static int rz_ssi_start(struct rz_ssi_priv *ssi, struct rz_ssi_stream *strm)
{
bool is_play = rz_ssi_stream_is_play(strm->substream);
bool is_play = strm->substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
bool is_full_duplex;
u32 ssicr, ssifcr;
@@ -423,14 +420,6 @@ static int rz_ssi_stop(struct rz_ssi_priv *ssi, struct rz_ssi_stream *strm)
/* Disable TX/RX */
rz_ssi_reg_mask_setl(ssi, SSICR, SSICR_TEN | SSICR_REN, 0);
/* Cancel all remaining DMA transactions */
if (rz_ssi_is_dma_enabled(ssi)) {
if (ssi->playback.dma_ch)
dmaengine_terminate_async(ssi->playback.dma_ch);
if (ssi->capture.dma_ch)
dmaengine_terminate_async(ssi->capture.dma_ch);
}
rz_ssi_set_idle(ssi);
return 0;
@@ -458,10 +447,6 @@ static void rz_ssi_pointer_update(struct rz_ssi_stream *strm, int frames)
snd_pcm_period_elapsed(strm->substream);
strm->period_counter = current_period;
}
strm->completed_dma_buf_pos += runtime->period_size;
if (strm->completed_dma_buf_pos >= runtime->buffer_size)
strm->completed_dma_buf_pos = 0;
}
static int rz_ssi_pio_recv(struct rz_ssi_priv *ssi, struct rz_ssi_stream *strm)
@@ -606,12 +591,6 @@ static irqreturn_t rz_ssi_interrupt(int irq, void *data)
if (irq == ssi->irq_int) { /* error or idle */
bool is_stopped = !!(ssisr & (SSISR_RUIRQ | SSISR_ROIRQ |
SSISR_TUIRQ | SSISR_TOIRQ));
int i, count;
if (rz_ssi_is_dma_enabled(ssi))
count = 4;
else
count = 1;
if (ssi->capture.substream && is_stopped) {
if (ssisr & SSISR_RUIRQ)
@@ -631,19 +610,41 @@ static irqreturn_t rz_ssi_interrupt(int irq, void *data)
rz_ssi_stop(ssi, strm_playback);
}
if (!rz_ssi_is_stream_running(&ssi->playback) &&
!rz_ssi_is_stream_running(&ssi->capture) &&
rz_ssi_is_dma_enabled(ssi) && is_stopped) {
if (ssi->playback.substream &&
ssi->dmas[SNDRV_PCM_STREAM_PLAYBACK])
dmaengine_pause(ssi->dmas[SNDRV_PCM_STREAM_PLAYBACK]);
if (ssi->capture.substream &&
ssi->dmas[SNDRV_PCM_STREAM_CAPTURE] &&
/* Avoid calling pause twice in case of half duplex. */
ssi->dmas[SNDRV_PCM_STREAM_PLAYBACK] !=
ssi->dmas[SNDRV_PCM_STREAM_CAPTURE])
dmaengine_pause(ssi->dmas[SNDRV_PCM_STREAM_CAPTURE]);
}
/* Clear all flags */
rz_ssi_reg_mask_setl(ssi, SSISR, SSISR_TOIRQ | SSISR_TUIRQ |
SSISR_ROIRQ | SSISR_RUIRQ, 0);
/* Add/remove more data */
if (ssi->capture.substream && is_stopped) {
for (i = 0; i < count; i++)
if (rz_ssi_is_dma_enabled(ssi)) {
if (ssi->dmas[SNDRV_PCM_STREAM_CAPTURE])
dmaengine_resume(ssi->dmas[SNDRV_PCM_STREAM_CAPTURE]);
} else {
strm_capture->transfer(ssi, strm_capture);
}
}
if (ssi->playback.substream && is_stopped) {
for (i = 0; i < count; i++)
if (rz_ssi_is_dma_enabled(ssi)) {
if (ssi->dmas[SNDRV_PCM_STREAM_PLAYBACK])
dmaengine_resume(ssi->dmas[SNDRV_PCM_STREAM_PLAYBACK]);
} else {
strm_playback->transfer(ssi, strm_playback);
}
}
/* Resume */
@@ -679,153 +680,11 @@ static irqreturn_t rz_ssi_interrupt(int irq, void *data)
return IRQ_HANDLED;
}
static int rz_ssi_dma_slave_config(struct rz_ssi_priv *ssi,
struct dma_chan *dma_ch, bool is_play)
{
struct dma_slave_config cfg;
memset(&cfg, 0, sizeof(cfg));
cfg.direction = is_play ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM;
cfg.dst_addr = ssi->phys + SSIFTDR;
cfg.src_addr = ssi->phys + SSIFRDR;
if (ssi->hw_params_cache.sample_width == 16) {
cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
} else {
cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
}
return dmaengine_slave_config(dma_ch, &cfg);
}
static int rz_ssi_dma_transfer(struct rz_ssi_priv *ssi,
struct rz_ssi_stream *strm)
{
struct snd_pcm_substream *substream = strm->substream;
struct dma_async_tx_descriptor *desc;
struct snd_pcm_runtime *runtime;
enum dma_transfer_direction dir;
u32 dma_paddr, dma_size;
int amount;
if (!rz_ssi_stream_is_valid(ssi, strm))
return -EINVAL;
runtime = substream->runtime;
if (runtime->state == SNDRV_PCM_STATE_DRAINING)
/*
* Stream is ending, so do not queue up any more DMA
* transfers otherwise we play partial sound clips
* because we can't shut off the DMA quick enough.
*/
return 0;
dir = rz_ssi_stream_is_play(substream) ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM;
/* Always transfer 1 period */
amount = runtime->period_size;
/* DMA physical address and size */
dma_paddr = runtime->dma_addr + frames_to_bytes(runtime,
strm->dma_buffer_pos);
dma_size = frames_to_bytes(runtime, amount);
desc = dmaengine_prep_slave_single(strm->dma_ch, dma_paddr, dma_size,
dir,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
if (!desc) {
dev_err(ssi->dev, "dmaengine_prep_slave_single() fail\n");
return -ENOMEM;
}
desc->callback = rz_ssi_dma_complete;
desc->callback_param = strm;
if (dmaengine_submit(desc) < 0) {
dev_err(ssi->dev, "dmaengine_submit() fail\n");
return -EIO;
}
/* Update DMA pointer */
strm->dma_buffer_pos += amount;
if (strm->dma_buffer_pos >= runtime->buffer_size)
strm->dma_buffer_pos = 0;
/* Start DMA */
dma_async_issue_pending(strm->dma_ch);
return 0;
}
static void rz_ssi_dma_complete(void *data)
{
struct rz_ssi_stream *strm = (struct rz_ssi_stream *)data;
if (!strm->running || !strm->substream || !strm->substream->runtime)
return;
/* Note that next DMA transaction has probably already started */
rz_ssi_pointer_update(strm, strm->substream->runtime->period_size);
/* Queue up another DMA transaction */
rz_ssi_dma_transfer(strm->priv, strm);
}
static void rz_ssi_release_dma_channels(struct rz_ssi_priv *ssi)
{
if (ssi->playback.dma_ch) {
dma_release_channel(ssi->playback.dma_ch);
ssi->playback.dma_ch = NULL;
if (ssi->dma_rt)
ssi->dma_rt = false;
}
if (ssi->capture.dma_ch) {
dma_release_channel(ssi->capture.dma_ch);
ssi->capture.dma_ch = NULL;
}
}
static int rz_ssi_dma_request(struct rz_ssi_priv *ssi, struct device *dev)
{
ssi->playback.dma_ch = dma_request_chan(dev, "tx");
if (IS_ERR(ssi->playback.dma_ch))
ssi->playback.dma_ch = NULL;
ssi->capture.dma_ch = dma_request_chan(dev, "rx");
if (IS_ERR(ssi->capture.dma_ch))
ssi->capture.dma_ch = NULL;
if (!ssi->playback.dma_ch && !ssi->capture.dma_ch) {
ssi->playback.dma_ch = dma_request_chan(dev, "rt");
if (IS_ERR(ssi->playback.dma_ch)) {
ssi->playback.dma_ch = NULL;
goto no_dma;
}
ssi->dma_rt = true;
}
if (!rz_ssi_is_dma_enabled(ssi))
goto no_dma;
return 0;
no_dma:
rz_ssi_release_dma_channels(ssi);
return -ENODEV;
}
static int rz_ssi_trigger_resume(struct rz_ssi_priv *ssi, struct rz_ssi_stream *strm)
{
struct snd_pcm_substream *substream = strm->substream;
struct snd_pcm_runtime *runtime = substream->runtime;
int ret;
strm->dma_buffer_pos = strm->completed_dma_buf_pos + runtime->period_size;
if (rz_ssi_is_stream_running(&ssi->playback) ||
rz_ssi_is_stream_running(&ssi->capture))
return 0;
@@ -834,7 +693,7 @@ static int rz_ssi_trigger_resume(struct rz_ssi_priv *ssi, struct rz_ssi_stream *
if (ret)
return ret;
return rz_ssi_clk_setup(ssi, ssi->hw_params_cache.rate,
return rz_ssi_clk_setup(ssi, substream, ssi->hw_params_cache.rate,
ssi->hw_params_cache.channels);
}
@@ -843,10 +702,11 @@ static int rz_ssi_dai_trigger(struct snd_pcm_substream *substream, int cmd,
{
struct rz_ssi_priv *ssi = snd_soc_dai_get_drvdata(dai);
struct rz_ssi_stream *strm = rz_ssi_stream_get(ssi, substream);
int ret = 0, i, num_transfer = 1;
int ret = 0;
switch (cmd) {
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
ret = rz_ssi_trigger_resume(ssi, strm);
if (ret)
return ret;
@@ -857,28 +717,7 @@ static int rz_ssi_dai_trigger(struct snd_pcm_substream *substream, int cmd,
if (cmd == SNDRV_PCM_TRIGGER_START)
rz_ssi_stream_init(strm, substream);
if (rz_ssi_is_dma_enabled(ssi)) {
bool is_playback = rz_ssi_stream_is_play(substream);
if (ssi->dma_rt)
ret = rz_ssi_dma_slave_config(ssi, ssi->playback.dma_ch,
is_playback);
else
ret = rz_ssi_dma_slave_config(ssi, strm->dma_ch,
is_playback);
/* Fallback to pio */
if (ret < 0) {
ssi->playback.transfer = rz_ssi_pio_send;
ssi->capture.transfer = rz_ssi_pio_recv;
rz_ssi_release_dma_channels(ssi);
} else {
/* For DMA, queue up multiple DMA descriptors */
num_transfer = 4;
}
}
for (i = 0; i < num_transfer; i++) {
if (!rz_ssi_is_dma_enabled(ssi)) {
ret = strm->transfer(ssi, strm);
if (ret)
return ret;
@@ -888,6 +727,7 @@ static int rz_ssi_dai_trigger(struct snd_pcm_substream *substream, int cmd,
break;
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
rz_ssi_stop(ssi, strm);
break;
@@ -973,6 +813,8 @@ static void rz_ssi_shutdown(struct snd_pcm_substream *substream,
ssi->dup.tx_active = false;
else
ssi->dup.rx_active = false;
ssi->dmas[substream->stream] = NULL;
}
static bool rz_ssi_is_valid_hw_params(struct rz_ssi_priv *ssi, unsigned int rate,
@@ -1024,6 +866,12 @@ static int rz_ssi_dai_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
/* Save the DMA channels for recovery. */
if (rz_ssi_is_dma_enabled(ssi))
ssi->dmas[substream->stream] = snd_dmaengine_pcm_get_chan(substream);
else
ssi->dmas[substream->stream] = NULL;
if (rz_ssi_is_stream_running(&ssi->playback) ||
rz_ssi_is_stream_running(&ssi->capture)) {
if (rz_ssi_is_valid_hw_params(ssi, rate, channels, sample_width, sample_bits))
@@ -1039,10 +887,21 @@ static int rz_ssi_dai_hw_params(struct snd_pcm_substream *substream,
if (ret)
return ret;
return rz_ssi_clk_setup(ssi, rate, channels);
return rz_ssi_clk_setup(ssi, substream, rate, channels);
}
static int rz_ssi_dai_probe(struct snd_soc_dai *dai)
{
struct rz_ssi_priv *ssi = snd_soc_dai_get_drvdata(dai);
snd_soc_dai_init_dma_data(dai, &ssi->dma_dais[SNDRV_PCM_STREAM_PLAYBACK],
&ssi->dma_dais[SNDRV_PCM_STREAM_CAPTURE]);
return 0;
}
static const struct snd_soc_dai_ops rz_ssi_dai_ops = {
.probe = rz_ssi_dai_probe,
.startup = rz_ssi_startup,
.shutdown = rz_ssi_shutdown,
.trigger = rz_ssi_dai_trigger,
@@ -1054,10 +913,11 @@ static const struct snd_pcm_hardware rz_ssi_pcm_hardware = {
.info = SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_RESUME,
.buffer_bytes_max = PREALLOC_BUFFER,
SNDRV_PCM_INFO_RESUME |
SNDRV_PCM_INFO_PAUSE,
.buffer_bytes_max = 192 * 1024,
.period_bytes_min = 32,
.period_bytes_max = 8192,
.period_bytes_max = 48 * 1024,
.channels_min = SSI_CHAN_MIN,
.channels_max = SSI_CHAN_MAX,
.periods_min = 1,
@@ -1065,8 +925,8 @@ static const struct snd_pcm_hardware rz_ssi_pcm_hardware = {
.fifo_size = 32 * 2,
};
static int rz_ssi_pcm_open(struct snd_soc_component *component,
struct snd_pcm_substream *substream)
static int rz_ssi_pcm_open_pio(struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
snd_soc_set_runtime_hwparams(substream, &rz_ssi_pcm_hardware);
@@ -1074,6 +934,13 @@ static int rz_ssi_pcm_open(struct snd_soc_component *component,
SNDRV_PCM_HW_PARAM_PERIODS);
}
static int rz_ssi_pcm_open_dma(struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
return snd_pcm_hw_constraint_integer(substream->runtime,
SNDRV_PCM_HW_PARAM_PERIODS);
}
static snd_pcm_uframes_t rz_ssi_pcm_pointer(struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
@@ -1090,7 +957,8 @@ static int rz_ssi_pcm_new(struct snd_soc_component *component,
{
snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV,
rtd->card->snd_card->dev,
PREALLOC_BUFFER, PREALLOC_BUFFER_MAX);
rz_ssi_pcm_hardware.buffer_bytes_max,
rz_ssi_pcm_hardware.buffer_bytes_max);
return 0;
}
@@ -1113,16 +981,30 @@ static struct snd_soc_dai_driver rz_ssi_soc_dai[] = {
},
};
static const struct snd_soc_component_driver rz_ssi_soc_component = {
static const struct snd_soc_component_driver rz_ssi_soc_component_pio = {
.name = "rz-ssi",
.open = rz_ssi_pcm_open,
.open = rz_ssi_pcm_open_pio,
.pointer = rz_ssi_pcm_pointer,
.pcm_new = rz_ssi_pcm_new,
.legacy_dai_naming = 1,
};
static const struct snd_soc_component_driver rz_ssi_soc_component_dma = {
.name = "rz-ssi",
.open = rz_ssi_pcm_open_dma,
.legacy_dai_naming = 1,
};
static const struct snd_dmaengine_pcm_config rz_ssi_dmaengine_pcm_conf = {
.pcm_hardware = &rz_ssi_pcm_hardware,
.prealloc_buffer_size = 192 * 1024,
.prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
};
static int rz_ssi_probe(struct platform_device *pdev)
{
const struct snd_soc_component_driver *component_driver;
struct device_node *np = pdev->dev.of_node;
struct device *dev = &pdev->dev;
struct rz_ssi_priv *ssi;
struct clk *audio_clk;
@@ -1138,7 +1020,6 @@ static int rz_ssi_probe(struct platform_device *pdev)
if (IS_ERR(ssi->base))
return PTR_ERR(ssi->base);
ssi->phys = res->start;
ssi->clk = devm_clk_get(dev, "ssi");
if (IS_ERR(ssi->clk))
return PTR_ERR(ssi->clk);
@@ -1162,16 +1043,43 @@ static int rz_ssi_probe(struct platform_device *pdev)
ssi->audio_mck = ssi->audio_clk_1 ? ssi->audio_clk_1 : ssi->audio_clk_2;
/* Detect DMA support */
ret = rz_ssi_dma_request(ssi, dev);
if (ret < 0) {
ssi->dma_dais[SNDRV_PCM_STREAM_PLAYBACK].addr = (dma_addr_t)res->start + SSIFTDR;
ssi->dma_dais[SNDRV_PCM_STREAM_CAPTURE].addr = (dma_addr_t)res->start + SSIFRDR;
if (of_property_present(np, "dma-names")) {
struct snd_dmaengine_pcm_config *config;
unsigned int flags = 0;
config = devm_kzalloc(dev, sizeof(*config), GFP_KERNEL);
if (!config)
return -ENOMEM;
config->pcm_hardware = rz_ssi_dmaengine_pcm_conf.pcm_hardware;
config->prealloc_buffer_size = rz_ssi_dmaengine_pcm_conf.prealloc_buffer_size;
config->prepare_slave_config = rz_ssi_dmaengine_pcm_conf.prepare_slave_config;
if (of_property_match_string(np, "dma-names", "rt") == 0) {
flags = SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX;
config->chan_names[SNDRV_PCM_STREAM_PLAYBACK] = "rt";
} else {
config->chan_names[SNDRV_PCM_STREAM_PLAYBACK] = "tx";
config->chan_names[SNDRV_PCM_STREAM_CAPTURE] = "rx";
}
ret = devm_snd_dmaengine_pcm_register(&pdev->dev, config, flags);
} else {
ret = -ENODEV;
}
if (ret == -EPROBE_DEFER) {
return ret;
} else if (ret) {
dev_warn(dev, "DMA not available, using PIO\n");
ssi->playback.transfer = rz_ssi_pio_send;
ssi->capture.transfer = rz_ssi_pio_recv;
component_driver = &rz_ssi_soc_component_pio;
} else {
dev_info(dev, "DMA enabled");
ssi->playback.transfer = rz_ssi_dma_transfer;
ssi->capture.transfer = rz_ssi_dma_transfer;
dev_info(dev, "DMA enabled\n");
component_driver = &rz_ssi_soc_component_dma;
}
ssi->playback.priv = ssi;
@@ -1182,17 +1090,13 @@ static int rz_ssi_probe(struct platform_device *pdev)
/* Error Interrupt */
ssi->irq_int = platform_get_irq_byname(pdev, "int_req");
if (ssi->irq_int < 0) {
ret = ssi->irq_int;
goto err_release_dma_chs;
}
if (ssi->irq_int < 0)
return ssi->irq_int;
ret = devm_request_irq(dev, ssi->irq_int, rz_ssi_interrupt,
0, dev_name(dev), ssi);
if (ret < 0) {
dev_err_probe(dev, ret, "irq request error (int_req)\n");
goto err_release_dma_chs;
}
if (ret < 0)
return dev_err_probe(dev, ret, "irq request error (int_req)\n");
if (!rz_ssi_is_dma_enabled(ssi)) {
/* Tx and Rx interrupts (pio only) */
@@ -1233,43 +1137,19 @@ static int rz_ssi_probe(struct platform_device *pdev)
}
ssi->rstc = devm_reset_control_get_exclusive(dev, NULL);
if (IS_ERR(ssi->rstc)) {
ret = PTR_ERR(ssi->rstc);
goto err_release_dma_chs;
}
if (IS_ERR(ssi->rstc))
return dev_err_probe(dev, PTR_ERR(ssi->rstc), "Failed to get reset\n");
/* Default 0 for power saving. Can be overridden via sysfs. */
pm_runtime_set_autosuspend_delay(dev, 0);
pm_runtime_use_autosuspend(dev);
ret = devm_pm_runtime_enable(dev);
if (ret < 0) {
dev_err(dev, "Failed to enable runtime PM!\n");
goto err_release_dma_chs;
}
if (ret < 0)
return dev_err_probe(dev, ret, "Failed to enable runtime PM!\n");
ret = devm_snd_soc_register_component(dev, &rz_ssi_soc_component,
rz_ssi_soc_dai,
ARRAY_SIZE(rz_ssi_soc_dai));
if (ret < 0) {
dev_err(dev, "failed to register snd component\n");
goto err_release_dma_chs;
}
return 0;
err_release_dma_chs:
rz_ssi_release_dma_channels(ssi);
return ret;
}
static void rz_ssi_remove(struct platform_device *pdev)
{
struct rz_ssi_priv *ssi = dev_get_drvdata(&pdev->dev);
rz_ssi_release_dma_channels(ssi);
reset_control_assert(ssi->rstc);
return devm_snd_soc_register_component(dev, component_driver,
rz_ssi_soc_dai,
ARRAY_SIZE(rz_ssi_soc_dai));
}
static const struct of_device_id rz_ssi_of_match[] = {
@@ -1304,7 +1184,6 @@ static struct platform_driver rz_ssi_driver = {
.pm = pm_ptr(&rz_ssi_pm_ops),
},
.probe = rz_ssi_probe,
.remove = rz_ssi_remove,
};
module_platform_driver(rz_ssi_driver);