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