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