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