xref: /linux/sound/soc/loongson/loongson_i2s_pci.c (revision d84881e06836dc1655777a592b4279be76ad7324)
1*d84881e0SYingkun Meng // SPDX-License-Identifier: GPL-2.0
2*d84881e0SYingkun Meng //
3*d84881e0SYingkun Meng // loongson_i2s_pci.c -- Loongson I2S controller driver
4*d84881e0SYingkun Meng //
5*d84881e0SYingkun Meng // Copyright (C) 2023 Loongson Technology Corporation Limited
6*d84881e0SYingkun Meng // Author: Yingkun Meng <mengyingkun@loongson.cn>
7*d84881e0SYingkun Meng //
8*d84881e0SYingkun Meng 
9*d84881e0SYingkun Meng #include <linux/module.h>
10*d84881e0SYingkun Meng #include <linux/delay.h>
11*d84881e0SYingkun Meng #include <linux/pm_runtime.h>
12*d84881e0SYingkun Meng #include <linux/dma-mapping.h>
13*d84881e0SYingkun Meng #include <linux/acpi.h>
14*d84881e0SYingkun Meng #include <linux/pci.h>
15*d84881e0SYingkun Meng #include <sound/soc.h>
16*d84881e0SYingkun Meng #include "loongson_i2s.h"
17*d84881e0SYingkun Meng #include "loongson_dma.h"
18*d84881e0SYingkun Meng 
19*d84881e0SYingkun Meng static bool loongson_i2s_wr_reg(struct device *dev, unsigned int reg)
20*d84881e0SYingkun Meng {
21*d84881e0SYingkun Meng 	switch (reg) {
22*d84881e0SYingkun Meng 	case LS_I2S_CFG:
23*d84881e0SYingkun Meng 	case LS_I2S_CTRL:
24*d84881e0SYingkun Meng 	case LS_I2S_RX_DATA:
25*d84881e0SYingkun Meng 	case LS_I2S_TX_DATA:
26*d84881e0SYingkun Meng 	case LS_I2S_CFG1:
27*d84881e0SYingkun Meng 		return true;
28*d84881e0SYingkun Meng 	default:
29*d84881e0SYingkun Meng 		return false;
30*d84881e0SYingkun Meng 	};
31*d84881e0SYingkun Meng }
32*d84881e0SYingkun Meng 
33*d84881e0SYingkun Meng static bool loongson_i2s_rd_reg(struct device *dev, unsigned int reg)
34*d84881e0SYingkun Meng {
35*d84881e0SYingkun Meng 	switch (reg) {
36*d84881e0SYingkun Meng 	case LS_I2S_VER:
37*d84881e0SYingkun Meng 	case LS_I2S_CFG:
38*d84881e0SYingkun Meng 	case LS_I2S_CTRL:
39*d84881e0SYingkun Meng 	case LS_I2S_RX_DATA:
40*d84881e0SYingkun Meng 	case LS_I2S_TX_DATA:
41*d84881e0SYingkun Meng 	case LS_I2S_CFG1:
42*d84881e0SYingkun Meng 		return true;
43*d84881e0SYingkun Meng 	default:
44*d84881e0SYingkun Meng 		return false;
45*d84881e0SYingkun Meng 	};
46*d84881e0SYingkun Meng }
47*d84881e0SYingkun Meng 
48*d84881e0SYingkun Meng static bool loongson_i2s_volatile_reg(struct device *dev, unsigned int reg)
49*d84881e0SYingkun Meng {
50*d84881e0SYingkun Meng 	switch (reg) {
51*d84881e0SYingkun Meng 	case LS_I2S_CFG:
52*d84881e0SYingkun Meng 	case LS_I2S_CTRL:
53*d84881e0SYingkun Meng 	case LS_I2S_RX_DATA:
54*d84881e0SYingkun Meng 	case LS_I2S_TX_DATA:
55*d84881e0SYingkun Meng 	case LS_I2S_CFG1:
56*d84881e0SYingkun Meng 		return true;
57*d84881e0SYingkun Meng 	default:
58*d84881e0SYingkun Meng 		return false;
59*d84881e0SYingkun Meng 	};
60*d84881e0SYingkun Meng }
61*d84881e0SYingkun Meng 
62*d84881e0SYingkun Meng static const struct regmap_config loongson_i2s_regmap_config = {
63*d84881e0SYingkun Meng 	.reg_bits = 32,
64*d84881e0SYingkun Meng 	.reg_stride = 4,
65*d84881e0SYingkun Meng 	.val_bits = 32,
66*d84881e0SYingkun Meng 	.max_register = LS_I2S_CFG1,
67*d84881e0SYingkun Meng 	.writeable_reg = loongson_i2s_wr_reg,
68*d84881e0SYingkun Meng 	.readable_reg = loongson_i2s_rd_reg,
69*d84881e0SYingkun Meng 	.volatile_reg = loongson_i2s_volatile_reg,
70*d84881e0SYingkun Meng 	.cache_type = REGCACHE_FLAT,
71*d84881e0SYingkun Meng };
72*d84881e0SYingkun Meng 
73*d84881e0SYingkun Meng static int loongson_i2s_pci_probe(struct pci_dev *pdev,
74*d84881e0SYingkun Meng 				  const struct pci_device_id *pid)
75*d84881e0SYingkun Meng {
76*d84881e0SYingkun Meng 	const struct fwnode_handle *fwnode = pdev->dev.fwnode;
77*d84881e0SYingkun Meng 	struct loongson_dma_data *tx_data, *rx_data;
78*d84881e0SYingkun Meng 	struct loongson_i2s *i2s;
79*d84881e0SYingkun Meng 	int ret;
80*d84881e0SYingkun Meng 
81*d84881e0SYingkun Meng 	if (pcim_enable_device(pdev)) {
82*d84881e0SYingkun Meng 		dev_err(&pdev->dev, "pci_enable_device failed\n");
83*d84881e0SYingkun Meng 		return -ENODEV;
84*d84881e0SYingkun Meng 	}
85*d84881e0SYingkun Meng 
86*d84881e0SYingkun Meng 	i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL);
87*d84881e0SYingkun Meng 	if (!i2s)
88*d84881e0SYingkun Meng 		return -ENOMEM;
89*d84881e0SYingkun Meng 
90*d84881e0SYingkun Meng 	i2s->rev_id = pdev->revision;
91*d84881e0SYingkun Meng 	i2s->dev = &pdev->dev;
92*d84881e0SYingkun Meng 	pci_set_drvdata(pdev, i2s);
93*d84881e0SYingkun Meng 
94*d84881e0SYingkun Meng 	ret = pcim_iomap_regions(pdev, 1 << 0, dev_name(&pdev->dev));
95*d84881e0SYingkun Meng 	if (ret < 0) {
96*d84881e0SYingkun Meng 		dev_err(&pdev->dev, "iomap_regions failed\n");
97*d84881e0SYingkun Meng 		return ret;
98*d84881e0SYingkun Meng 	}
99*d84881e0SYingkun Meng 	i2s->reg_base = pcim_iomap_table(pdev)[0];
100*d84881e0SYingkun Meng 	i2s->regmap = devm_regmap_init_mmio(&pdev->dev, i2s->reg_base,
101*d84881e0SYingkun Meng 					    &loongson_i2s_regmap_config);
102*d84881e0SYingkun Meng 	if (IS_ERR(i2s->regmap)) {
103*d84881e0SYingkun Meng 		dev_err(&pdev->dev, "regmap_init_mmio failed\n");
104*d84881e0SYingkun Meng 		return PTR_ERR(i2s->regmap);
105*d84881e0SYingkun Meng 	}
106*d84881e0SYingkun Meng 
107*d84881e0SYingkun Meng 	tx_data = &i2s->tx_dma_data;
108*d84881e0SYingkun Meng 	rx_data = &i2s->rx_dma_data;
109*d84881e0SYingkun Meng 
110*d84881e0SYingkun Meng 	tx_data->dev_addr = (dma_addr_t)i2s->reg_base + LS_I2S_TX_DATA;
111*d84881e0SYingkun Meng 	tx_data->order_addr = i2s->reg_base + LS_I2S_TX_ORDER;
112*d84881e0SYingkun Meng 
113*d84881e0SYingkun Meng 	rx_data->dev_addr = (dma_addr_t)i2s->reg_base + LS_I2S_RX_DATA;
114*d84881e0SYingkun Meng 	rx_data->order_addr = i2s->reg_base + LS_I2S_RX_ORDER;
115*d84881e0SYingkun Meng 
116*d84881e0SYingkun Meng 	tx_data->irq = fwnode_irq_get_byname(fwnode, "tx");
117*d84881e0SYingkun Meng 	if (tx_data->irq < 0) {
118*d84881e0SYingkun Meng 		dev_err(&pdev->dev, "dma tx irq invalid\n");
119*d84881e0SYingkun Meng 		return tx_data->irq;
120*d84881e0SYingkun Meng 	}
121*d84881e0SYingkun Meng 
122*d84881e0SYingkun Meng 	rx_data->irq = fwnode_irq_get_byname(fwnode, "rx");
123*d84881e0SYingkun Meng 	if (rx_data->irq < 0) {
124*d84881e0SYingkun Meng 		dev_err(&pdev->dev, "dma rx irq invalid\n");
125*d84881e0SYingkun Meng 		return rx_data->irq;
126*d84881e0SYingkun Meng 	}
127*d84881e0SYingkun Meng 
128*d84881e0SYingkun Meng 	device_property_read_u32(&pdev->dev, "clock-frequency", &i2s->clk_rate);
129*d84881e0SYingkun Meng 	if (!i2s->clk_rate) {
130*d84881e0SYingkun Meng 		dev_err(&pdev->dev, "clock-frequency property invalid\n");
131*d84881e0SYingkun Meng 		return -EINVAL;
132*d84881e0SYingkun Meng 	}
133*d84881e0SYingkun Meng 
134*d84881e0SYingkun Meng 	dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
135*d84881e0SYingkun Meng 
136*d84881e0SYingkun Meng 	if (i2s->rev_id == 1) {
137*d84881e0SYingkun Meng 		regmap_write(i2s->regmap, LS_I2S_CTRL, I2S_CTRL_RESET);
138*d84881e0SYingkun Meng 		udelay(200);
139*d84881e0SYingkun Meng 	}
140*d84881e0SYingkun Meng 
141*d84881e0SYingkun Meng 	ret = devm_snd_soc_register_component(&pdev->dev,
142*d84881e0SYingkun Meng 					      &loongson_i2s_component,
143*d84881e0SYingkun Meng 					      &loongson_i2s_dai, 1);
144*d84881e0SYingkun Meng 	if (ret) {
145*d84881e0SYingkun Meng 		dev_err(&pdev->dev, "register DAI failed %d\n", ret);
146*d84881e0SYingkun Meng 		return ret;
147*d84881e0SYingkun Meng 	}
148*d84881e0SYingkun Meng 
149*d84881e0SYingkun Meng 	return 0;
150*d84881e0SYingkun Meng }
151*d84881e0SYingkun Meng 
152*d84881e0SYingkun Meng static const struct pci_device_id loongson_i2s_ids[] = {
153*d84881e0SYingkun Meng 	{ PCI_DEVICE(PCI_VENDOR_ID_LOONGSON, 0x7a27) },
154*d84881e0SYingkun Meng 	{ },
155*d84881e0SYingkun Meng };
156*d84881e0SYingkun Meng MODULE_DEVICE_TABLE(pci, loongson_i2s_ids);
157*d84881e0SYingkun Meng 
158*d84881e0SYingkun Meng static struct pci_driver loongson_i2s_driver = {
159*d84881e0SYingkun Meng 	.name = "loongson-i2s-pci",
160*d84881e0SYingkun Meng 	.id_table = loongson_i2s_ids,
161*d84881e0SYingkun Meng 	.probe = loongson_i2s_pci_probe,
162*d84881e0SYingkun Meng 	.driver = {
163*d84881e0SYingkun Meng 		.owner = THIS_MODULE,
164*d84881e0SYingkun Meng 		.pm = pm_sleep_ptr(&loongson_i2s_pm),
165*d84881e0SYingkun Meng 	},
166*d84881e0SYingkun Meng };
167*d84881e0SYingkun Meng module_pci_driver(loongson_i2s_driver);
168*d84881e0SYingkun Meng 
169*d84881e0SYingkun Meng MODULE_DESCRIPTION("Loongson I2S Master Mode ASoC Driver");
170*d84881e0SYingkun Meng MODULE_AUTHOR("Loongson Technology Corporation Limited");
171*d84881e0SYingkun Meng MODULE_LICENSE("GPL");
172