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