18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Implementation of primary ALSA driver code base for NVIDIA Tegra HDA. 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/clk.h> 88c2ecf20Sopenharmony_ci#include <linux/clocksource.h> 98c2ecf20Sopenharmony_ci#include <linux/completion.h> 108c2ecf20Sopenharmony_ci#include <linux/delay.h> 118c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 128c2ecf20Sopenharmony_ci#include <linux/init.h> 138c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 148c2ecf20Sopenharmony_ci#include <linux/io.h> 158c2ecf20Sopenharmony_ci#include <linux/kernel.h> 168c2ecf20Sopenharmony_ci#include <linux/module.h> 178c2ecf20Sopenharmony_ci#include <linux/moduleparam.h> 188c2ecf20Sopenharmony_ci#include <linux/mutex.h> 198c2ecf20Sopenharmony_ci#include <linux/of_device.h> 208c2ecf20Sopenharmony_ci#include <linux/reset.h> 218c2ecf20Sopenharmony_ci#include <linux/slab.h> 228c2ecf20Sopenharmony_ci#include <linux/time.h> 238c2ecf20Sopenharmony_ci#include <linux/string.h> 248c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#include <sound/core.h> 278c2ecf20Sopenharmony_ci#include <sound/initval.h> 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#include <sound/hda_codec.h> 308c2ecf20Sopenharmony_ci#include "hda_controller.h" 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci/* Defines for Nvidia Tegra HDA support */ 338c2ecf20Sopenharmony_ci#define HDA_BAR0 0x8000 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#define HDA_CFG_CMD 0x1004 368c2ecf20Sopenharmony_ci#define HDA_CFG_BAR0 0x1010 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci#define HDA_ENABLE_IO_SPACE (1 << 0) 398c2ecf20Sopenharmony_ci#define HDA_ENABLE_MEM_SPACE (1 << 1) 408c2ecf20Sopenharmony_ci#define HDA_ENABLE_BUS_MASTER (1 << 2) 418c2ecf20Sopenharmony_ci#define HDA_ENABLE_SERR (1 << 8) 428c2ecf20Sopenharmony_ci#define HDA_DISABLE_INTR (1 << 10) 438c2ecf20Sopenharmony_ci#define HDA_BAR0_INIT_PROGRAM 0xFFFFFFFF 448c2ecf20Sopenharmony_ci#define HDA_BAR0_FINAL_PROGRAM (1 << 14) 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci/* IPFS */ 478c2ecf20Sopenharmony_ci#define HDA_IPFS_CONFIG 0x180 488c2ecf20Sopenharmony_ci#define HDA_IPFS_EN_FPCI 0x1 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci#define HDA_IPFS_FPCI_BAR0 0x80 518c2ecf20Sopenharmony_ci#define HDA_FPCI_BAR0_START 0x40 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci#define HDA_IPFS_INTR_MASK 0x188 548c2ecf20Sopenharmony_ci#define HDA_IPFS_EN_INTR (1 << 16) 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci/* FPCI */ 578c2ecf20Sopenharmony_ci#define FPCI_DBG_CFG_2 0x10F4 588c2ecf20Sopenharmony_ci#define FPCI_GCAP_NSDO_SHIFT 18 598c2ecf20Sopenharmony_ci#define FPCI_GCAP_NSDO_MASK (0x3 << FPCI_GCAP_NSDO_SHIFT) 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci/* max number of SDs */ 628c2ecf20Sopenharmony_ci#define NUM_CAPTURE_SD 1 638c2ecf20Sopenharmony_ci#define NUM_PLAYBACK_SD 1 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci/* 668c2ecf20Sopenharmony_ci * Tegra194 does not reflect correct number of SDO lines. Below macro 678c2ecf20Sopenharmony_ci * is used to update the GCAP register to workaround the issue. 688c2ecf20Sopenharmony_ci */ 698c2ecf20Sopenharmony_ci#define TEGRA194_NUM_SDO_LINES 4 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistruct hda_tegra { 728c2ecf20Sopenharmony_ci struct azx chip; 738c2ecf20Sopenharmony_ci struct device *dev; 748c2ecf20Sopenharmony_ci struct reset_control *reset; 758c2ecf20Sopenharmony_ci struct clk_bulk_data clocks[3]; 768c2ecf20Sopenharmony_ci unsigned int nclocks; 778c2ecf20Sopenharmony_ci void __iomem *regs; 788c2ecf20Sopenharmony_ci struct work_struct probe_work; 798c2ecf20Sopenharmony_ci}; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 828c2ecf20Sopenharmony_cistatic int power_save = CONFIG_SND_HDA_POWER_SAVE_DEFAULT; 838c2ecf20Sopenharmony_cimodule_param(power_save, bint, 0644); 848c2ecf20Sopenharmony_ciMODULE_PARM_DESC(power_save, 858c2ecf20Sopenharmony_ci "Automatic power-saving timeout (in seconds, 0 = disable)."); 868c2ecf20Sopenharmony_ci#else 878c2ecf20Sopenharmony_ci#define power_save 0 888c2ecf20Sopenharmony_ci#endif 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cistatic const struct hda_controller_ops hda_tegra_ops; /* nothing special */ 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_cistatic void hda_tegra_init(struct hda_tegra *hda) 938c2ecf20Sopenharmony_ci{ 948c2ecf20Sopenharmony_ci u32 v; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci /* Enable PCI access */ 978c2ecf20Sopenharmony_ci v = readl(hda->regs + HDA_IPFS_CONFIG); 988c2ecf20Sopenharmony_ci v |= HDA_IPFS_EN_FPCI; 998c2ecf20Sopenharmony_ci writel(v, hda->regs + HDA_IPFS_CONFIG); 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci /* Enable MEM/IO space and bus master */ 1028c2ecf20Sopenharmony_ci v = readl(hda->regs + HDA_CFG_CMD); 1038c2ecf20Sopenharmony_ci v &= ~HDA_DISABLE_INTR; 1048c2ecf20Sopenharmony_ci v |= HDA_ENABLE_MEM_SPACE | HDA_ENABLE_IO_SPACE | 1058c2ecf20Sopenharmony_ci HDA_ENABLE_BUS_MASTER | HDA_ENABLE_SERR; 1068c2ecf20Sopenharmony_ci writel(v, hda->regs + HDA_CFG_CMD); 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci writel(HDA_BAR0_INIT_PROGRAM, hda->regs + HDA_CFG_BAR0); 1098c2ecf20Sopenharmony_ci writel(HDA_BAR0_FINAL_PROGRAM, hda->regs + HDA_CFG_BAR0); 1108c2ecf20Sopenharmony_ci writel(HDA_FPCI_BAR0_START, hda->regs + HDA_IPFS_FPCI_BAR0); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci v = readl(hda->regs + HDA_IPFS_INTR_MASK); 1138c2ecf20Sopenharmony_ci v |= HDA_IPFS_EN_INTR; 1148c2ecf20Sopenharmony_ci writel(v, hda->regs + HDA_IPFS_INTR_MASK); 1158c2ecf20Sopenharmony_ci} 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci/* 1188c2ecf20Sopenharmony_ci * power management 1198c2ecf20Sopenharmony_ci */ 1208c2ecf20Sopenharmony_cistatic int __maybe_unused hda_tegra_suspend(struct device *dev) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci struct snd_card *card = dev_get_drvdata(dev); 1238c2ecf20Sopenharmony_ci int rc; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci rc = pm_runtime_force_suspend(dev); 1268c2ecf20Sopenharmony_ci if (rc < 0) 1278c2ecf20Sopenharmony_ci return rc; 1288c2ecf20Sopenharmony_ci snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci return 0; 1318c2ecf20Sopenharmony_ci} 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_cistatic int __maybe_unused hda_tegra_resume(struct device *dev) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci struct snd_card *card = dev_get_drvdata(dev); 1368c2ecf20Sopenharmony_ci int rc; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci rc = pm_runtime_force_resume(dev); 1398c2ecf20Sopenharmony_ci if (rc < 0) 1408c2ecf20Sopenharmony_ci return rc; 1418c2ecf20Sopenharmony_ci snd_power_change_state(card, SNDRV_CTL_POWER_D0); 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci return 0; 1448c2ecf20Sopenharmony_ci} 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_cistatic int __maybe_unused hda_tegra_runtime_suspend(struct device *dev) 1478c2ecf20Sopenharmony_ci{ 1488c2ecf20Sopenharmony_ci struct snd_card *card = dev_get_drvdata(dev); 1498c2ecf20Sopenharmony_ci struct azx *chip = card->private_data; 1508c2ecf20Sopenharmony_ci struct hda_tegra *hda = container_of(chip, struct hda_tegra, chip); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci if (chip && chip->running) { 1538c2ecf20Sopenharmony_ci /* enable controller wake up event */ 1548c2ecf20Sopenharmony_ci azx_writew(chip, WAKEEN, azx_readw(chip, WAKEEN) | 1558c2ecf20Sopenharmony_ci STATESTS_INT_MASK); 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci azx_stop_chip(chip); 1588c2ecf20Sopenharmony_ci azx_enter_link_reset(chip); 1598c2ecf20Sopenharmony_ci } 1608c2ecf20Sopenharmony_ci clk_bulk_disable_unprepare(hda->nclocks, hda->clocks); 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci return 0; 1638c2ecf20Sopenharmony_ci} 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_cistatic int __maybe_unused hda_tegra_runtime_resume(struct device *dev) 1668c2ecf20Sopenharmony_ci{ 1678c2ecf20Sopenharmony_ci struct snd_card *card = dev_get_drvdata(dev); 1688c2ecf20Sopenharmony_ci struct azx *chip = card->private_data; 1698c2ecf20Sopenharmony_ci struct hda_tegra *hda = container_of(chip, struct hda_tegra, chip); 1708c2ecf20Sopenharmony_ci int rc; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci if (!chip->running) { 1738c2ecf20Sopenharmony_ci rc = reset_control_assert(hda->reset); 1748c2ecf20Sopenharmony_ci if (rc) 1758c2ecf20Sopenharmony_ci return rc; 1768c2ecf20Sopenharmony_ci } 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci rc = clk_bulk_prepare_enable(hda->nclocks, hda->clocks); 1798c2ecf20Sopenharmony_ci if (rc != 0) 1808c2ecf20Sopenharmony_ci return rc; 1818c2ecf20Sopenharmony_ci if (chip && chip->running) { 1828c2ecf20Sopenharmony_ci hda_tegra_init(hda); 1838c2ecf20Sopenharmony_ci azx_init_chip(chip, 1); 1848c2ecf20Sopenharmony_ci /* disable controller wake up event*/ 1858c2ecf20Sopenharmony_ci azx_writew(chip, WAKEEN, azx_readw(chip, WAKEEN) & 1868c2ecf20Sopenharmony_ci ~STATESTS_INT_MASK); 1878c2ecf20Sopenharmony_ci } else { 1888c2ecf20Sopenharmony_ci usleep_range(10, 100); 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci rc = reset_control_deassert(hda->reset); 1918c2ecf20Sopenharmony_ci if (rc) 1928c2ecf20Sopenharmony_ci return rc; 1938c2ecf20Sopenharmony_ci } 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci return 0; 1968c2ecf20Sopenharmony_ci} 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_cistatic const struct dev_pm_ops hda_tegra_pm = { 1998c2ecf20Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(hda_tegra_suspend, hda_tegra_resume) 2008c2ecf20Sopenharmony_ci SET_RUNTIME_PM_OPS(hda_tegra_runtime_suspend, 2018c2ecf20Sopenharmony_ci hda_tegra_runtime_resume, 2028c2ecf20Sopenharmony_ci NULL) 2038c2ecf20Sopenharmony_ci}; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_cistatic int hda_tegra_dev_disconnect(struct snd_device *device) 2068c2ecf20Sopenharmony_ci{ 2078c2ecf20Sopenharmony_ci struct azx *chip = device->device_data; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci chip->bus.shutdown = 1; 2108c2ecf20Sopenharmony_ci return 0; 2118c2ecf20Sopenharmony_ci} 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci/* 2148c2ecf20Sopenharmony_ci * destructor 2158c2ecf20Sopenharmony_ci */ 2168c2ecf20Sopenharmony_cistatic int hda_tegra_dev_free(struct snd_device *device) 2178c2ecf20Sopenharmony_ci{ 2188c2ecf20Sopenharmony_ci struct azx *chip = device->device_data; 2198c2ecf20Sopenharmony_ci struct hda_tegra *hda = container_of(chip, struct hda_tegra, chip); 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci cancel_work_sync(&hda->probe_work); 2228c2ecf20Sopenharmony_ci if (azx_bus(chip)->chip_init) { 2238c2ecf20Sopenharmony_ci azx_stop_all_streams(chip); 2248c2ecf20Sopenharmony_ci azx_stop_chip(chip); 2258c2ecf20Sopenharmony_ci } 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci azx_free_stream_pages(chip); 2288c2ecf20Sopenharmony_ci azx_free_streams(chip); 2298c2ecf20Sopenharmony_ci snd_hdac_bus_exit(azx_bus(chip)); 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci return 0; 2328c2ecf20Sopenharmony_ci} 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_cistatic int hda_tegra_init_chip(struct azx *chip, struct platform_device *pdev) 2358c2ecf20Sopenharmony_ci{ 2368c2ecf20Sopenharmony_ci struct hda_tegra *hda = container_of(chip, struct hda_tegra, chip); 2378c2ecf20Sopenharmony_ci struct hdac_bus *bus = azx_bus(chip); 2388c2ecf20Sopenharmony_ci struct device *dev = hda->dev; 2398c2ecf20Sopenharmony_ci struct resource *res; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 2428c2ecf20Sopenharmony_ci hda->regs = devm_ioremap_resource(dev, res); 2438c2ecf20Sopenharmony_ci if (IS_ERR(hda->regs)) 2448c2ecf20Sopenharmony_ci return PTR_ERR(hda->regs); 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci bus->remap_addr = hda->regs + HDA_BAR0; 2478c2ecf20Sopenharmony_ci bus->addr = res->start + HDA_BAR0; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci hda_tegra_init(hda); 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci return 0; 2528c2ecf20Sopenharmony_ci} 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_cistatic int hda_tegra_first_init(struct azx *chip, struct platform_device *pdev) 2558c2ecf20Sopenharmony_ci{ 2568c2ecf20Sopenharmony_ci struct hda_tegra *hda = container_of(chip, struct hda_tegra, chip); 2578c2ecf20Sopenharmony_ci struct hdac_bus *bus = azx_bus(chip); 2588c2ecf20Sopenharmony_ci struct snd_card *card = chip->card; 2598c2ecf20Sopenharmony_ci int err; 2608c2ecf20Sopenharmony_ci unsigned short gcap; 2618c2ecf20Sopenharmony_ci int irq_id = platform_get_irq(pdev, 0); 2628c2ecf20Sopenharmony_ci const char *sname, *drv_name = "tegra-hda"; 2638c2ecf20Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci if (irq_id < 0) 2668c2ecf20Sopenharmony_ci return irq_id; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci err = hda_tegra_init_chip(chip, pdev); 2698c2ecf20Sopenharmony_ci if (err) 2708c2ecf20Sopenharmony_ci return err; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci err = devm_request_irq(chip->card->dev, irq_id, azx_interrupt, 2738c2ecf20Sopenharmony_ci IRQF_SHARED, KBUILD_MODNAME, chip); 2748c2ecf20Sopenharmony_ci if (err) { 2758c2ecf20Sopenharmony_ci dev_err(chip->card->dev, 2768c2ecf20Sopenharmony_ci "unable to request IRQ %d, disabling device\n", 2778c2ecf20Sopenharmony_ci irq_id); 2788c2ecf20Sopenharmony_ci return err; 2798c2ecf20Sopenharmony_ci } 2808c2ecf20Sopenharmony_ci bus->irq = irq_id; 2818c2ecf20Sopenharmony_ci bus->dma_stop_delay = 100; 2828c2ecf20Sopenharmony_ci card->sync_irq = bus->irq; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci /* 2858c2ecf20Sopenharmony_ci * Tegra194 has 4 SDO lines and the STRIPE can be used to 2868c2ecf20Sopenharmony_ci * indicate how many of the SDO lines the stream should be 2878c2ecf20Sopenharmony_ci * striped. But GCAP register does not reflect the true 2888c2ecf20Sopenharmony_ci * capability of HW. Below workaround helps to fix this. 2898c2ecf20Sopenharmony_ci * 2908c2ecf20Sopenharmony_ci * GCAP_NSDO is bits 19:18 in T_AZA_DBG_CFG_2, 2918c2ecf20Sopenharmony_ci * 0 for 1 SDO, 1 for 2 SDO, 2 for 4 SDO lines. 2928c2ecf20Sopenharmony_ci */ 2938c2ecf20Sopenharmony_ci if (of_device_is_compatible(np, "nvidia,tegra194-hda")) { 2948c2ecf20Sopenharmony_ci u32 val; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci dev_info(card->dev, "Override SDO lines to %u\n", 2978c2ecf20Sopenharmony_ci TEGRA194_NUM_SDO_LINES); 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci val = readl(hda->regs + FPCI_DBG_CFG_2) & ~FPCI_GCAP_NSDO_MASK; 3008c2ecf20Sopenharmony_ci val |= (TEGRA194_NUM_SDO_LINES >> 1) << FPCI_GCAP_NSDO_SHIFT; 3018c2ecf20Sopenharmony_ci writel(val, hda->regs + FPCI_DBG_CFG_2); 3028c2ecf20Sopenharmony_ci } 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci gcap = azx_readw(chip, GCAP); 3058c2ecf20Sopenharmony_ci dev_dbg(card->dev, "chipset global capabilities = 0x%x\n", gcap); 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci chip->align_buffer_size = 1; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci /* read number of streams from GCAP register instead of using 3108c2ecf20Sopenharmony_ci * hardcoded value 3118c2ecf20Sopenharmony_ci */ 3128c2ecf20Sopenharmony_ci chip->capture_streams = (gcap >> 8) & 0x0f; 3138c2ecf20Sopenharmony_ci chip->playback_streams = (gcap >> 12) & 0x0f; 3148c2ecf20Sopenharmony_ci if (!chip->playback_streams && !chip->capture_streams) { 3158c2ecf20Sopenharmony_ci /* gcap didn't give any info, switching to old method */ 3168c2ecf20Sopenharmony_ci chip->playback_streams = NUM_PLAYBACK_SD; 3178c2ecf20Sopenharmony_ci chip->capture_streams = NUM_CAPTURE_SD; 3188c2ecf20Sopenharmony_ci } 3198c2ecf20Sopenharmony_ci chip->capture_index_offset = 0; 3208c2ecf20Sopenharmony_ci chip->playback_index_offset = chip->capture_streams; 3218c2ecf20Sopenharmony_ci chip->num_streams = chip->playback_streams + chip->capture_streams; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci /* initialize streams */ 3248c2ecf20Sopenharmony_ci err = azx_init_streams(chip); 3258c2ecf20Sopenharmony_ci if (err < 0) { 3268c2ecf20Sopenharmony_ci dev_err(card->dev, "failed to initialize streams: %d\n", err); 3278c2ecf20Sopenharmony_ci return err; 3288c2ecf20Sopenharmony_ci } 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci err = azx_alloc_stream_pages(chip); 3318c2ecf20Sopenharmony_ci if (err < 0) { 3328c2ecf20Sopenharmony_ci dev_err(card->dev, "failed to allocate stream pages: %d\n", 3338c2ecf20Sopenharmony_ci err); 3348c2ecf20Sopenharmony_ci return err; 3358c2ecf20Sopenharmony_ci } 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci /* initialize chip */ 3388c2ecf20Sopenharmony_ci azx_init_chip(chip, 1); 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci /* 3418c2ecf20Sopenharmony_ci * Playback (for 44.1K/48K, 2-channel, 16-bps) fails with 3428c2ecf20Sopenharmony_ci * 4 SDO lines due to legacy design limitation. Following 3438c2ecf20Sopenharmony_ci * is, from HD Audio Specification (Revision 1.0a), used to 3448c2ecf20Sopenharmony_ci * control striping of the stream across multiple SDO lines 3458c2ecf20Sopenharmony_ci * for sample rates <= 48K. 3468c2ecf20Sopenharmony_ci * 3478c2ecf20Sopenharmony_ci * { ((num_channels * bits_per_sample) / number of SDOs) >= 8 } 3488c2ecf20Sopenharmony_ci * 3498c2ecf20Sopenharmony_ci * Due to legacy design issue it is recommended that above 3508c2ecf20Sopenharmony_ci * ratio must be greater than 8. Since number of SDO lines is 3518c2ecf20Sopenharmony_ci * in powers of 2, next available ratio is 16 which can be 3528c2ecf20Sopenharmony_ci * used as a limiting factor here. 3538c2ecf20Sopenharmony_ci */ 3548c2ecf20Sopenharmony_ci if (of_device_is_compatible(np, "nvidia,tegra30-hda")) 3558c2ecf20Sopenharmony_ci chip->bus.core.sdo_limit = 16; 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci /* codec detection */ 3588c2ecf20Sopenharmony_ci if (!bus->codec_mask) { 3598c2ecf20Sopenharmony_ci dev_err(card->dev, "no codecs found!\n"); 3608c2ecf20Sopenharmony_ci return -ENODEV; 3618c2ecf20Sopenharmony_ci } 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci /* driver name */ 3648c2ecf20Sopenharmony_ci strncpy(card->driver, drv_name, sizeof(card->driver)); 3658c2ecf20Sopenharmony_ci /* shortname for card */ 3668c2ecf20Sopenharmony_ci sname = of_get_property(np, "nvidia,model", NULL); 3678c2ecf20Sopenharmony_ci if (!sname) 3688c2ecf20Sopenharmony_ci sname = drv_name; 3698c2ecf20Sopenharmony_ci if (strlen(sname) > sizeof(card->shortname)) 3708c2ecf20Sopenharmony_ci dev_info(card->dev, "truncating shortname for card\n"); 3718c2ecf20Sopenharmony_ci strncpy(card->shortname, sname, sizeof(card->shortname)); 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci /* longname for card */ 3748c2ecf20Sopenharmony_ci snprintf(card->longname, sizeof(card->longname), 3758c2ecf20Sopenharmony_ci "%s at 0x%lx irq %i", 3768c2ecf20Sopenharmony_ci card->shortname, bus->addr, bus->irq); 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci return 0; 3798c2ecf20Sopenharmony_ci} 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci/* 3828c2ecf20Sopenharmony_ci * constructor 3838c2ecf20Sopenharmony_ci */ 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_cistatic void hda_tegra_probe_work(struct work_struct *work); 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_cistatic int hda_tegra_create(struct snd_card *card, 3888c2ecf20Sopenharmony_ci unsigned int driver_caps, 3898c2ecf20Sopenharmony_ci struct hda_tegra *hda) 3908c2ecf20Sopenharmony_ci{ 3918c2ecf20Sopenharmony_ci static const struct snd_device_ops ops = { 3928c2ecf20Sopenharmony_ci .dev_disconnect = hda_tegra_dev_disconnect, 3938c2ecf20Sopenharmony_ci .dev_free = hda_tegra_dev_free, 3948c2ecf20Sopenharmony_ci }; 3958c2ecf20Sopenharmony_ci struct azx *chip; 3968c2ecf20Sopenharmony_ci int err; 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci chip = &hda->chip; 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci mutex_init(&chip->open_mutex); 4018c2ecf20Sopenharmony_ci chip->card = card; 4028c2ecf20Sopenharmony_ci chip->ops = &hda_tegra_ops; 4038c2ecf20Sopenharmony_ci chip->driver_caps = driver_caps; 4048c2ecf20Sopenharmony_ci chip->driver_type = driver_caps & 0xff; 4058c2ecf20Sopenharmony_ci chip->dev_index = 0; 4068c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&chip->pcm_list); 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci chip->codec_probe_mask = -1; 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci chip->single_cmd = false; 4118c2ecf20Sopenharmony_ci chip->snoop = true; 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci INIT_WORK(&hda->probe_work, hda_tegra_probe_work); 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci err = azx_bus_init(chip, NULL); 4168c2ecf20Sopenharmony_ci if (err < 0) 4178c2ecf20Sopenharmony_ci return err; 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci chip->bus.core.sync_write = 0; 4208c2ecf20Sopenharmony_ci chip->bus.core.needs_damn_long_delay = 1; 4218c2ecf20Sopenharmony_ci chip->bus.core.aligned_mmio = 1; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops); 4248c2ecf20Sopenharmony_ci if (err < 0) { 4258c2ecf20Sopenharmony_ci dev_err(card->dev, "Error creating device\n"); 4268c2ecf20Sopenharmony_ci return err; 4278c2ecf20Sopenharmony_ci } 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci return 0; 4308c2ecf20Sopenharmony_ci} 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_cistatic const struct of_device_id hda_tegra_match[] = { 4338c2ecf20Sopenharmony_ci { .compatible = "nvidia,tegra30-hda" }, 4348c2ecf20Sopenharmony_ci { .compatible = "nvidia,tegra194-hda" }, 4358c2ecf20Sopenharmony_ci {}, 4368c2ecf20Sopenharmony_ci}; 4378c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, hda_tegra_match); 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_cistatic int hda_tegra_probe(struct platform_device *pdev) 4408c2ecf20Sopenharmony_ci{ 4418c2ecf20Sopenharmony_ci const unsigned int driver_flags = AZX_DCAPS_CORBRP_SELF_CLEAR | 4428c2ecf20Sopenharmony_ci AZX_DCAPS_PM_RUNTIME | 4438c2ecf20Sopenharmony_ci AZX_DCAPS_4K_BDLE_BOUNDARY; 4448c2ecf20Sopenharmony_ci struct snd_card *card; 4458c2ecf20Sopenharmony_ci struct azx *chip; 4468c2ecf20Sopenharmony_ci struct hda_tegra *hda; 4478c2ecf20Sopenharmony_ci int err; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci hda = devm_kzalloc(&pdev->dev, sizeof(*hda), GFP_KERNEL); 4508c2ecf20Sopenharmony_ci if (!hda) 4518c2ecf20Sopenharmony_ci return -ENOMEM; 4528c2ecf20Sopenharmony_ci hda->dev = &pdev->dev; 4538c2ecf20Sopenharmony_ci chip = &hda->chip; 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci err = snd_card_new(&pdev->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, 4568c2ecf20Sopenharmony_ci THIS_MODULE, 0, &card); 4578c2ecf20Sopenharmony_ci if (err < 0) { 4588c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Error creating card!\n"); 4598c2ecf20Sopenharmony_ci return err; 4608c2ecf20Sopenharmony_ci } 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci hda->reset = devm_reset_control_array_get_exclusive(&pdev->dev); 4638c2ecf20Sopenharmony_ci if (IS_ERR(hda->reset)) { 4648c2ecf20Sopenharmony_ci err = PTR_ERR(hda->reset); 4658c2ecf20Sopenharmony_ci goto out_free; 4668c2ecf20Sopenharmony_ci } 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci hda->clocks[hda->nclocks++].id = "hda"; 4698c2ecf20Sopenharmony_ci hda->clocks[hda->nclocks++].id = "hda2hdmi"; 4708c2ecf20Sopenharmony_ci hda->clocks[hda->nclocks++].id = "hda2codec_2x"; 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci err = devm_clk_bulk_get(&pdev->dev, hda->nclocks, hda->clocks); 4738c2ecf20Sopenharmony_ci if (err < 0) 4748c2ecf20Sopenharmony_ci goto out_free; 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci err = hda_tegra_create(card, driver_flags, hda); 4778c2ecf20Sopenharmony_ci if (err < 0) 4788c2ecf20Sopenharmony_ci goto out_free; 4798c2ecf20Sopenharmony_ci card->private_data = chip; 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci dev_set_drvdata(&pdev->dev, card); 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci pm_runtime_enable(hda->dev); 4848c2ecf20Sopenharmony_ci if (!azx_has_pm_runtime(chip)) 4858c2ecf20Sopenharmony_ci pm_runtime_forbid(hda->dev); 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci schedule_work(&hda->probe_work); 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci return 0; 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ciout_free: 4928c2ecf20Sopenharmony_ci snd_card_free(card); 4938c2ecf20Sopenharmony_ci return err; 4948c2ecf20Sopenharmony_ci} 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_cistatic void hda_tegra_probe_work(struct work_struct *work) 4978c2ecf20Sopenharmony_ci{ 4988c2ecf20Sopenharmony_ci struct hda_tegra *hda = container_of(work, struct hda_tegra, probe_work); 4998c2ecf20Sopenharmony_ci struct azx *chip = &hda->chip; 5008c2ecf20Sopenharmony_ci struct platform_device *pdev = to_platform_device(hda->dev); 5018c2ecf20Sopenharmony_ci int err; 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci pm_runtime_get_sync(hda->dev); 5048c2ecf20Sopenharmony_ci err = hda_tegra_first_init(chip, pdev); 5058c2ecf20Sopenharmony_ci if (err < 0) 5068c2ecf20Sopenharmony_ci goto out_free; 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci /* create codec instances */ 5098c2ecf20Sopenharmony_ci err = azx_probe_codecs(chip, 8); 5108c2ecf20Sopenharmony_ci if (err < 0) 5118c2ecf20Sopenharmony_ci goto out_free; 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci err = azx_codec_configure(chip); 5148c2ecf20Sopenharmony_ci if (err < 0) 5158c2ecf20Sopenharmony_ci goto out_free; 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci err = snd_card_register(chip->card); 5188c2ecf20Sopenharmony_ci if (err < 0) 5198c2ecf20Sopenharmony_ci goto out_free; 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci chip->running = 1; 5228c2ecf20Sopenharmony_ci snd_hda_set_power_save(&chip->bus, power_save * 1000); 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci out_free: 5258c2ecf20Sopenharmony_ci pm_runtime_put(hda->dev); 5268c2ecf20Sopenharmony_ci return; /* no error return from async probe */ 5278c2ecf20Sopenharmony_ci} 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_cistatic int hda_tegra_remove(struct platform_device *pdev) 5308c2ecf20Sopenharmony_ci{ 5318c2ecf20Sopenharmony_ci int ret; 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci ret = snd_card_free(dev_get_drvdata(&pdev->dev)); 5348c2ecf20Sopenharmony_ci pm_runtime_disable(&pdev->dev); 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci return ret; 5378c2ecf20Sopenharmony_ci} 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_cistatic void hda_tegra_shutdown(struct platform_device *pdev) 5408c2ecf20Sopenharmony_ci{ 5418c2ecf20Sopenharmony_ci struct snd_card *card = dev_get_drvdata(&pdev->dev); 5428c2ecf20Sopenharmony_ci struct azx *chip; 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci if (!card) 5458c2ecf20Sopenharmony_ci return; 5468c2ecf20Sopenharmony_ci chip = card->private_data; 5478c2ecf20Sopenharmony_ci if (chip && chip->running) 5488c2ecf20Sopenharmony_ci azx_stop_chip(chip); 5498c2ecf20Sopenharmony_ci} 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_cistatic struct platform_driver tegra_platform_hda = { 5528c2ecf20Sopenharmony_ci .driver = { 5538c2ecf20Sopenharmony_ci .name = "tegra-hda", 5548c2ecf20Sopenharmony_ci .pm = &hda_tegra_pm, 5558c2ecf20Sopenharmony_ci .of_match_table = hda_tegra_match, 5568c2ecf20Sopenharmony_ci }, 5578c2ecf20Sopenharmony_ci .probe = hda_tegra_probe, 5588c2ecf20Sopenharmony_ci .remove = hda_tegra_remove, 5598c2ecf20Sopenharmony_ci .shutdown = hda_tegra_shutdown, 5608c2ecf20Sopenharmony_ci}; 5618c2ecf20Sopenharmony_cimodule_platform_driver(tegra_platform_hda); 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Tegra HDA bus driver"); 5648c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 565