summaryrefslogtreecommitdiffstats
path: root/sound/soc/loongson/loongson_i2s_pci.c
blob: fa90361865c6cfcace51bb12b2d78738bb790d0a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
// SPDX-License-Identifier: GPL-2.0
//
// loongson_i2s_pci.c -- Loongson I2S controller driver
//
// Copyright (C) 2023 Loongson Technology Corporation Limited
// Author: Yingkun Meng <mengyingkun@loongson.cn>
//

#include <linux/module.h>
#include <linux/delay.h>
#include <linux/pm_runtime.h>
#include <linux/dma-mapping.h>
#include <linux/acpi.h>
#include <linux/pci.h>
#include <sound/soc.h>
#include "loongson_i2s.h"
#include "loongson_dma.h"

static bool loongson_i2s_wr_reg(struct device *dev, unsigned int reg)
{
	switch (reg) {
	case LS_I2S_CFG:
	case LS_I2S_CTRL:
	case LS_I2S_RX_DATA:
	case LS_I2S_TX_DATA:
	case LS_I2S_CFG1:
		return true;
	default:
		return false;
	};
}

static bool loongson_i2s_rd_reg(struct device *dev, unsigned int reg)
{
	switch (reg) {
	case LS_I2S_VER:
	case LS_I2S_CFG:
	case LS_I2S_CTRL:
	case LS_I2S_RX_DATA:
	case LS_I2S_TX_DATA:
	case LS_I2S_CFG1:
		return true;
	default:
		return false;
	};
}

static bool loongson_i2s_volatile_reg(struct device *dev, unsigned int reg)
{
	switch (reg) {
	case LS_I2S_CFG:
	case LS_I2S_CTRL:
	case LS_I2S_RX_DATA:
	case LS_I2S_TX_DATA:
	case LS_I2S_CFG1:
		return true;
	default:
		return false;
	};
}

static const struct regmap_config loongson_i2s_regmap_config = {
	.reg_bits = 32,
	.reg_stride = 4,
	.val_bits = 32,
	.max_register = LS_I2S_CFG1,
	.writeable_reg = loongson_i2s_wr_reg,
	.readable_reg = loongson_i2s_rd_reg,
	.volatile_reg = loongson_i2s_volatile_reg,
	.cache_type = REGCACHE_FLAT,
};

static int loongson_i2s_pci_probe(struct pci_dev *pdev,
				  const struct pci_device_id *pid)
{
	const struct fwnode_handle *fwnode = pdev->dev.fwnode;
	struct loongson_dma_data *tx_data, *rx_data;
	struct loongson_i2s *i2s;
	int ret;

	if (pcim_enable_device(pdev)) {
		dev_err(&pdev->dev, "pci_enable_device failed\n");
		return -ENODEV;
	}

	i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL);
	if (!i2s)
		return -ENOMEM;

	i2s->rev_id = pdev->revision;
	i2s->dev = &pdev->dev;
	pci_set_drvdata(pdev, i2s);

	ret = pcim_iomap_regions(pdev, 1 << 0, dev_name(&pdev->dev));
	if (ret < 0) {
		dev_err(&pdev->dev, "iomap_regions failed\n");
		return ret;
	}
	i2s->reg_base = pcim_iomap_table(pdev)[0];
	i2s->regmap = devm_regmap_init_mmio(&pdev->dev, i2s->reg_base,
					    &loongson_i2s_regmap_config);
	if (IS_ERR(i2s->regmap)) {
		dev_err(&pdev->dev, "regmap_init_mmio failed\n");
		return PTR_ERR(i2s->regmap);
	}

	tx_data = &i2s->tx_dma_data;
	rx_data = &i2s->rx_dma_data;

	tx_data->dev_addr = pci_resource_start(pdev, 0) + LS_I2S_TX_DATA;
	tx_data->order_addr = i2s->reg_base + LS_I2S_TX_ORDER;

	rx_data->dev_addr = pci_resource_start(pdev, 0) + LS_I2S_RX_DATA;
	rx_data->order_addr = i2s->reg_base + LS_I2S_RX_ORDER;

	tx_data->irq = fwnode_irq_get_byname(fwnode, "tx");
	if (tx_data->irq < 0) {
		dev_err(&pdev->dev, "dma tx irq invalid\n");
		return tx_data->irq;
	}

	rx_data->irq = fwnode_irq_get_byname(fwnode, "rx");
	if (rx_data->irq < 0) {
		dev_err(&pdev->dev, "dma rx irq invalid\n");
		return rx_data->irq;
	}

	device_property_read_u32(&pdev->dev, "clock-frequency", &i2s->clk_rate);
	if (!i2s->clk_rate) {
		dev_err(&pdev->dev, "clock-frequency property invalid\n");
		return -EINVAL;
	}

	dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));

	if (i2s->rev_id == 1) {
		regmap_write(i2s->regmap, LS_I2S_CTRL, I2S_CTRL_RESET);
		udelay(200);
	}

	ret = devm_snd_soc_register_component(&pdev->dev,
					      &loongson_i2s_component,
					      &loongson_i2s_dai, 1);
	if (ret) {
		dev_err(&pdev->dev, "register DAI failed %d\n", ret);
		return ret;
	}

	return 0;
}

static const struct pci_device_id loongson_i2s_ids[] = {
	{ PCI_DEVICE(PCI_VENDOR_ID_LOONGSON, 0x7a27) },
	{ },
};
MODULE_DEVICE_TABLE(pci, loongson_i2s_ids);

static struct pci_driver loongson_i2s_driver = {
	.name = "loongson-i2s-pci",
	.id_table = loongson_i2s_ids,
	.probe = loongson_i2s_pci_probe,
	.driver = {
		.owner = THIS_MODULE,
		.pm = pm_sleep_ptr(&loongson_i2s_pm),
	},
};
module_pci_driver(loongson_i2s_driver);

MODULE_DESCRIPTION("Loongson I2S Master Mode ASoC Driver");
MODULE_AUTHOR("Loongson Technology Corporation Limited");
MODULE_LICENSE("GPL");