162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Implementation of primary ALSA driver code base for NVIDIA Tegra HDA. 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/clk.h> 862306a36Sopenharmony_ci#include <linux/clocksource.h> 962306a36Sopenharmony_ci#include <linux/completion.h> 1062306a36Sopenharmony_ci#include <linux/delay.h> 1162306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1262306a36Sopenharmony_ci#include <linux/init.h> 1362306a36Sopenharmony_ci#include <linux/interrupt.h> 1462306a36Sopenharmony_ci#include <linux/io.h> 1562306a36Sopenharmony_ci#include <linux/kernel.h> 1662306a36Sopenharmony_ci#include <linux/module.h> 1762306a36Sopenharmony_ci#include <linux/moduleparam.h> 1862306a36Sopenharmony_ci#include <linux/mutex.h> 1962306a36Sopenharmony_ci#include <linux/of.h> 2062306a36Sopenharmony_ci#include <linux/platform_device.h> 2162306a36Sopenharmony_ci#include <linux/reset.h> 2262306a36Sopenharmony_ci#include <linux/slab.h> 2362306a36Sopenharmony_ci#include <linux/time.h> 2462306a36Sopenharmony_ci#include <linux/string.h> 2562306a36Sopenharmony_ci#include <linux/pm_runtime.h> 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#include <sound/core.h> 2862306a36Sopenharmony_ci#include <sound/initval.h> 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#include <sound/hda_codec.h> 3162306a36Sopenharmony_ci#include "hda_controller.h" 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci/* Defines for Nvidia Tegra HDA support */ 3462306a36Sopenharmony_ci#define HDA_BAR0 0x8000 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#define HDA_CFG_CMD 0x1004 3762306a36Sopenharmony_ci#define HDA_CFG_BAR0 0x1010 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#define HDA_ENABLE_IO_SPACE (1 << 0) 4062306a36Sopenharmony_ci#define HDA_ENABLE_MEM_SPACE (1 << 1) 4162306a36Sopenharmony_ci#define HDA_ENABLE_BUS_MASTER (1 << 2) 4262306a36Sopenharmony_ci#define HDA_ENABLE_SERR (1 << 8) 4362306a36Sopenharmony_ci#define HDA_DISABLE_INTR (1 << 10) 4462306a36Sopenharmony_ci#define HDA_BAR0_INIT_PROGRAM 0xFFFFFFFF 4562306a36Sopenharmony_ci#define HDA_BAR0_FINAL_PROGRAM (1 << 14) 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci/* IPFS */ 4862306a36Sopenharmony_ci#define HDA_IPFS_CONFIG 0x180 4962306a36Sopenharmony_ci#define HDA_IPFS_EN_FPCI 0x1 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci#define HDA_IPFS_FPCI_BAR0 0x80 5262306a36Sopenharmony_ci#define HDA_FPCI_BAR0_START 0x40 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci#define HDA_IPFS_INTR_MASK 0x188 5562306a36Sopenharmony_ci#define HDA_IPFS_EN_INTR (1 << 16) 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci/* FPCI */ 5862306a36Sopenharmony_ci#define FPCI_DBG_CFG_2 0x10F4 5962306a36Sopenharmony_ci#define FPCI_GCAP_NSDO_SHIFT 18 6062306a36Sopenharmony_ci#define FPCI_GCAP_NSDO_MASK (0x3 << FPCI_GCAP_NSDO_SHIFT) 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci/* max number of SDs */ 6362306a36Sopenharmony_ci#define NUM_CAPTURE_SD 1 6462306a36Sopenharmony_ci#define NUM_PLAYBACK_SD 1 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci/* 6762306a36Sopenharmony_ci * Tegra194 does not reflect correct number of SDO lines. Below macro 6862306a36Sopenharmony_ci * is used to update the GCAP register to workaround the issue. 6962306a36Sopenharmony_ci */ 7062306a36Sopenharmony_ci#define TEGRA194_NUM_SDO_LINES 4 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistruct hda_tegra_soc { 7362306a36Sopenharmony_ci bool has_hda2codec_2x_reset; 7462306a36Sopenharmony_ci bool has_hda2hdmi; 7562306a36Sopenharmony_ci}; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistruct hda_tegra { 7862306a36Sopenharmony_ci struct azx chip; 7962306a36Sopenharmony_ci struct device *dev; 8062306a36Sopenharmony_ci struct reset_control_bulk_data resets[3]; 8162306a36Sopenharmony_ci struct clk_bulk_data clocks[3]; 8262306a36Sopenharmony_ci unsigned int nresets; 8362306a36Sopenharmony_ci unsigned int nclocks; 8462306a36Sopenharmony_ci void __iomem *regs; 8562306a36Sopenharmony_ci struct work_struct probe_work; 8662306a36Sopenharmony_ci const struct hda_tegra_soc *soc; 8762306a36Sopenharmony_ci}; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci#ifdef CONFIG_PM 9062306a36Sopenharmony_cistatic int power_save = CONFIG_SND_HDA_POWER_SAVE_DEFAULT; 9162306a36Sopenharmony_cimodule_param(power_save, bint, 0644); 9262306a36Sopenharmony_ciMODULE_PARM_DESC(power_save, 9362306a36Sopenharmony_ci "Automatic power-saving timeout (in seconds, 0 = disable)."); 9462306a36Sopenharmony_ci#else 9562306a36Sopenharmony_ci#define power_save 0 9662306a36Sopenharmony_ci#endif 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistatic const struct hda_controller_ops hda_tegra_ops; /* nothing special */ 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cistatic void hda_tegra_init(struct hda_tegra *hda) 10162306a36Sopenharmony_ci{ 10262306a36Sopenharmony_ci u32 v; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci /* Enable PCI access */ 10562306a36Sopenharmony_ci v = readl(hda->regs + HDA_IPFS_CONFIG); 10662306a36Sopenharmony_ci v |= HDA_IPFS_EN_FPCI; 10762306a36Sopenharmony_ci writel(v, hda->regs + HDA_IPFS_CONFIG); 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci /* Enable MEM/IO space and bus master */ 11062306a36Sopenharmony_ci v = readl(hda->regs + HDA_CFG_CMD); 11162306a36Sopenharmony_ci v &= ~HDA_DISABLE_INTR; 11262306a36Sopenharmony_ci v |= HDA_ENABLE_MEM_SPACE | HDA_ENABLE_IO_SPACE | 11362306a36Sopenharmony_ci HDA_ENABLE_BUS_MASTER | HDA_ENABLE_SERR; 11462306a36Sopenharmony_ci writel(v, hda->regs + HDA_CFG_CMD); 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci writel(HDA_BAR0_INIT_PROGRAM, hda->regs + HDA_CFG_BAR0); 11762306a36Sopenharmony_ci writel(HDA_BAR0_FINAL_PROGRAM, hda->regs + HDA_CFG_BAR0); 11862306a36Sopenharmony_ci writel(HDA_FPCI_BAR0_START, hda->regs + HDA_IPFS_FPCI_BAR0); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci v = readl(hda->regs + HDA_IPFS_INTR_MASK); 12162306a36Sopenharmony_ci v |= HDA_IPFS_EN_INTR; 12262306a36Sopenharmony_ci writel(v, hda->regs + HDA_IPFS_INTR_MASK); 12362306a36Sopenharmony_ci} 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci/* 12662306a36Sopenharmony_ci * power management 12762306a36Sopenharmony_ci */ 12862306a36Sopenharmony_cistatic int __maybe_unused hda_tegra_suspend(struct device *dev) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci struct snd_card *card = dev_get_drvdata(dev); 13162306a36Sopenharmony_ci int rc; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci rc = pm_runtime_force_suspend(dev); 13462306a36Sopenharmony_ci if (rc < 0) 13562306a36Sopenharmony_ci return rc; 13662306a36Sopenharmony_ci snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci return 0; 13962306a36Sopenharmony_ci} 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_cistatic int __maybe_unused hda_tegra_resume(struct device *dev) 14262306a36Sopenharmony_ci{ 14362306a36Sopenharmony_ci struct snd_card *card = dev_get_drvdata(dev); 14462306a36Sopenharmony_ci int rc; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci rc = pm_runtime_force_resume(dev); 14762306a36Sopenharmony_ci if (rc < 0) 14862306a36Sopenharmony_ci return rc; 14962306a36Sopenharmony_ci snd_power_change_state(card, SNDRV_CTL_POWER_D0); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci return 0; 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cistatic int __maybe_unused hda_tegra_runtime_suspend(struct device *dev) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci struct snd_card *card = dev_get_drvdata(dev); 15762306a36Sopenharmony_ci struct azx *chip = card->private_data; 15862306a36Sopenharmony_ci struct hda_tegra *hda = container_of(chip, struct hda_tegra, chip); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci if (chip && chip->running) { 16162306a36Sopenharmony_ci /* enable controller wake up event */ 16262306a36Sopenharmony_ci azx_writew(chip, WAKEEN, azx_readw(chip, WAKEEN) | 16362306a36Sopenharmony_ci STATESTS_INT_MASK); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci azx_stop_chip(chip); 16662306a36Sopenharmony_ci azx_enter_link_reset(chip); 16762306a36Sopenharmony_ci } 16862306a36Sopenharmony_ci clk_bulk_disable_unprepare(hda->nclocks, hda->clocks); 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci return 0; 17162306a36Sopenharmony_ci} 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_cistatic int __maybe_unused hda_tegra_runtime_resume(struct device *dev) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci struct snd_card *card = dev_get_drvdata(dev); 17662306a36Sopenharmony_ci struct azx *chip = card->private_data; 17762306a36Sopenharmony_ci struct hda_tegra *hda = container_of(chip, struct hda_tegra, chip); 17862306a36Sopenharmony_ci int rc; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci if (!chip->running) { 18162306a36Sopenharmony_ci rc = reset_control_bulk_assert(hda->nresets, hda->resets); 18262306a36Sopenharmony_ci if (rc) 18362306a36Sopenharmony_ci return rc; 18462306a36Sopenharmony_ci } 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci rc = clk_bulk_prepare_enable(hda->nclocks, hda->clocks); 18762306a36Sopenharmony_ci if (rc != 0) 18862306a36Sopenharmony_ci return rc; 18962306a36Sopenharmony_ci if (chip->running) { 19062306a36Sopenharmony_ci hda_tegra_init(hda); 19162306a36Sopenharmony_ci azx_init_chip(chip, 1); 19262306a36Sopenharmony_ci /* disable controller wake up event*/ 19362306a36Sopenharmony_ci azx_writew(chip, WAKEEN, azx_readw(chip, WAKEEN) & 19462306a36Sopenharmony_ci ~STATESTS_INT_MASK); 19562306a36Sopenharmony_ci } else { 19662306a36Sopenharmony_ci usleep_range(10, 100); 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci rc = reset_control_bulk_deassert(hda->nresets, hda->resets); 19962306a36Sopenharmony_ci if (rc) 20062306a36Sopenharmony_ci return rc; 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci return 0; 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_cistatic const struct dev_pm_ops hda_tegra_pm = { 20762306a36Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(hda_tegra_suspend, hda_tegra_resume) 20862306a36Sopenharmony_ci SET_RUNTIME_PM_OPS(hda_tegra_runtime_suspend, 20962306a36Sopenharmony_ci hda_tegra_runtime_resume, 21062306a36Sopenharmony_ci NULL) 21162306a36Sopenharmony_ci}; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_cistatic int hda_tegra_dev_disconnect(struct snd_device *device) 21462306a36Sopenharmony_ci{ 21562306a36Sopenharmony_ci struct azx *chip = device->device_data; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci chip->bus.shutdown = 1; 21862306a36Sopenharmony_ci return 0; 21962306a36Sopenharmony_ci} 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci/* 22262306a36Sopenharmony_ci * destructor 22362306a36Sopenharmony_ci */ 22462306a36Sopenharmony_cistatic int hda_tegra_dev_free(struct snd_device *device) 22562306a36Sopenharmony_ci{ 22662306a36Sopenharmony_ci struct azx *chip = device->device_data; 22762306a36Sopenharmony_ci struct hda_tegra *hda = container_of(chip, struct hda_tegra, chip); 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci cancel_work_sync(&hda->probe_work); 23062306a36Sopenharmony_ci if (azx_bus(chip)->chip_init) { 23162306a36Sopenharmony_ci azx_stop_all_streams(chip); 23262306a36Sopenharmony_ci azx_stop_chip(chip); 23362306a36Sopenharmony_ci } 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci azx_free_stream_pages(chip); 23662306a36Sopenharmony_ci azx_free_streams(chip); 23762306a36Sopenharmony_ci snd_hdac_bus_exit(azx_bus(chip)); 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci return 0; 24062306a36Sopenharmony_ci} 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_cistatic int hda_tegra_init_chip(struct azx *chip, struct platform_device *pdev) 24362306a36Sopenharmony_ci{ 24462306a36Sopenharmony_ci struct hda_tegra *hda = container_of(chip, struct hda_tegra, chip); 24562306a36Sopenharmony_ci struct hdac_bus *bus = azx_bus(chip); 24662306a36Sopenharmony_ci struct resource *res; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci hda->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res); 24962306a36Sopenharmony_ci if (IS_ERR(hda->regs)) 25062306a36Sopenharmony_ci return PTR_ERR(hda->regs); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci bus->remap_addr = hda->regs + HDA_BAR0; 25362306a36Sopenharmony_ci bus->addr = res->start + HDA_BAR0; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci hda_tegra_init(hda); 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci return 0; 25862306a36Sopenharmony_ci} 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_cistatic int hda_tegra_first_init(struct azx *chip, struct platform_device *pdev) 26162306a36Sopenharmony_ci{ 26262306a36Sopenharmony_ci struct hda_tegra *hda = container_of(chip, struct hda_tegra, chip); 26362306a36Sopenharmony_ci struct hdac_bus *bus = azx_bus(chip); 26462306a36Sopenharmony_ci struct snd_card *card = chip->card; 26562306a36Sopenharmony_ci int err; 26662306a36Sopenharmony_ci unsigned short gcap; 26762306a36Sopenharmony_ci int irq_id = platform_get_irq(pdev, 0); 26862306a36Sopenharmony_ci const char *sname, *drv_name = "tegra-hda"; 26962306a36Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci if (irq_id < 0) 27262306a36Sopenharmony_ci return irq_id; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci err = hda_tegra_init_chip(chip, pdev); 27562306a36Sopenharmony_ci if (err) 27662306a36Sopenharmony_ci return err; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci err = devm_request_irq(chip->card->dev, irq_id, azx_interrupt, 27962306a36Sopenharmony_ci IRQF_SHARED, KBUILD_MODNAME, chip); 28062306a36Sopenharmony_ci if (err) { 28162306a36Sopenharmony_ci dev_err(chip->card->dev, 28262306a36Sopenharmony_ci "unable to request IRQ %d, disabling device\n", 28362306a36Sopenharmony_ci irq_id); 28462306a36Sopenharmony_ci return err; 28562306a36Sopenharmony_ci } 28662306a36Sopenharmony_ci bus->irq = irq_id; 28762306a36Sopenharmony_ci bus->dma_stop_delay = 100; 28862306a36Sopenharmony_ci card->sync_irq = bus->irq; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci /* 29162306a36Sopenharmony_ci * Tegra194 has 4 SDO lines and the STRIPE can be used to 29262306a36Sopenharmony_ci * indicate how many of the SDO lines the stream should be 29362306a36Sopenharmony_ci * striped. But GCAP register does not reflect the true 29462306a36Sopenharmony_ci * capability of HW. Below workaround helps to fix this. 29562306a36Sopenharmony_ci * 29662306a36Sopenharmony_ci * GCAP_NSDO is bits 19:18 in T_AZA_DBG_CFG_2, 29762306a36Sopenharmony_ci * 0 for 1 SDO, 1 for 2 SDO, 2 for 4 SDO lines. 29862306a36Sopenharmony_ci */ 29962306a36Sopenharmony_ci if (of_device_is_compatible(np, "nvidia,tegra194-hda")) { 30062306a36Sopenharmony_ci u32 val; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci dev_info(card->dev, "Override SDO lines to %u\n", 30362306a36Sopenharmony_ci TEGRA194_NUM_SDO_LINES); 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci val = readl(hda->regs + FPCI_DBG_CFG_2) & ~FPCI_GCAP_NSDO_MASK; 30662306a36Sopenharmony_ci val |= (TEGRA194_NUM_SDO_LINES >> 1) << FPCI_GCAP_NSDO_SHIFT; 30762306a36Sopenharmony_ci writel(val, hda->regs + FPCI_DBG_CFG_2); 30862306a36Sopenharmony_ci } 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci gcap = azx_readw(chip, GCAP); 31162306a36Sopenharmony_ci dev_dbg(card->dev, "chipset global capabilities = 0x%x\n", gcap); 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci chip->align_buffer_size = 1; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci /* read number of streams from GCAP register instead of using 31662306a36Sopenharmony_ci * hardcoded value 31762306a36Sopenharmony_ci */ 31862306a36Sopenharmony_ci chip->capture_streams = (gcap >> 8) & 0x0f; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci /* The GCAP register on Tegra234 implies no Input Streams(ISS) support, 32162306a36Sopenharmony_ci * but the HW output stream descriptor programming should start with 32262306a36Sopenharmony_ci * offset 0x20*4 from base stream descriptor address. This will be a 32362306a36Sopenharmony_ci * problem while calculating the offset for output stream descriptor 32462306a36Sopenharmony_ci * which will be considering input stream also. So here output stream 32562306a36Sopenharmony_ci * starts with offset 0 which is wrong as HW register for output stream 32662306a36Sopenharmony_ci * offset starts with 4. 32762306a36Sopenharmony_ci */ 32862306a36Sopenharmony_ci if (of_device_is_compatible(np, "nvidia,tegra234-hda")) 32962306a36Sopenharmony_ci chip->capture_streams = 4; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci chip->playback_streams = (gcap >> 12) & 0x0f; 33262306a36Sopenharmony_ci if (!chip->playback_streams && !chip->capture_streams) { 33362306a36Sopenharmony_ci /* gcap didn't give any info, switching to old method */ 33462306a36Sopenharmony_ci chip->playback_streams = NUM_PLAYBACK_SD; 33562306a36Sopenharmony_ci chip->capture_streams = NUM_CAPTURE_SD; 33662306a36Sopenharmony_ci } 33762306a36Sopenharmony_ci chip->capture_index_offset = 0; 33862306a36Sopenharmony_ci chip->playback_index_offset = chip->capture_streams; 33962306a36Sopenharmony_ci chip->num_streams = chip->playback_streams + chip->capture_streams; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci /* initialize streams */ 34262306a36Sopenharmony_ci err = azx_init_streams(chip); 34362306a36Sopenharmony_ci if (err < 0) { 34462306a36Sopenharmony_ci dev_err(card->dev, "failed to initialize streams: %d\n", err); 34562306a36Sopenharmony_ci return err; 34662306a36Sopenharmony_ci } 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci err = azx_alloc_stream_pages(chip); 34962306a36Sopenharmony_ci if (err < 0) { 35062306a36Sopenharmony_ci dev_err(card->dev, "failed to allocate stream pages: %d\n", 35162306a36Sopenharmony_ci err); 35262306a36Sopenharmony_ci return err; 35362306a36Sopenharmony_ci } 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci /* initialize chip */ 35662306a36Sopenharmony_ci azx_init_chip(chip, 1); 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci /* 35962306a36Sopenharmony_ci * Playback (for 44.1K/48K, 2-channel, 16-bps) fails with 36062306a36Sopenharmony_ci * 4 SDO lines due to legacy design limitation. Following 36162306a36Sopenharmony_ci * is, from HD Audio Specification (Revision 1.0a), used to 36262306a36Sopenharmony_ci * control striping of the stream across multiple SDO lines 36362306a36Sopenharmony_ci * for sample rates <= 48K. 36462306a36Sopenharmony_ci * 36562306a36Sopenharmony_ci * { ((num_channels * bits_per_sample) / number of SDOs) >= 8 } 36662306a36Sopenharmony_ci * 36762306a36Sopenharmony_ci * Due to legacy design issue it is recommended that above 36862306a36Sopenharmony_ci * ratio must be greater than 8. Since number of SDO lines is 36962306a36Sopenharmony_ci * in powers of 2, next available ratio is 16 which can be 37062306a36Sopenharmony_ci * used as a limiting factor here. 37162306a36Sopenharmony_ci */ 37262306a36Sopenharmony_ci if (of_device_is_compatible(np, "nvidia,tegra30-hda")) 37362306a36Sopenharmony_ci chip->bus.core.sdo_limit = 16; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci /* codec detection */ 37662306a36Sopenharmony_ci if (!bus->codec_mask) { 37762306a36Sopenharmony_ci dev_err(card->dev, "no codecs found!\n"); 37862306a36Sopenharmony_ci return -ENODEV; 37962306a36Sopenharmony_ci } 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci /* driver name */ 38262306a36Sopenharmony_ci strscpy(card->driver, drv_name, sizeof(card->driver)); 38362306a36Sopenharmony_ci /* shortname for card */ 38462306a36Sopenharmony_ci sname = of_get_property(np, "nvidia,model", NULL); 38562306a36Sopenharmony_ci if (!sname) 38662306a36Sopenharmony_ci sname = drv_name; 38762306a36Sopenharmony_ci if (strlen(sname) > sizeof(card->shortname)) 38862306a36Sopenharmony_ci dev_info(card->dev, "truncating shortname for card\n"); 38962306a36Sopenharmony_ci strscpy(card->shortname, sname, sizeof(card->shortname)); 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci /* longname for card */ 39262306a36Sopenharmony_ci snprintf(card->longname, sizeof(card->longname), 39362306a36Sopenharmony_ci "%s at 0x%lx irq %i", 39462306a36Sopenharmony_ci card->shortname, bus->addr, bus->irq); 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci return 0; 39762306a36Sopenharmony_ci} 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci/* 40062306a36Sopenharmony_ci * constructor 40162306a36Sopenharmony_ci */ 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_cistatic void hda_tegra_probe_work(struct work_struct *work); 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_cistatic int hda_tegra_create(struct snd_card *card, 40662306a36Sopenharmony_ci unsigned int driver_caps, 40762306a36Sopenharmony_ci struct hda_tegra *hda) 40862306a36Sopenharmony_ci{ 40962306a36Sopenharmony_ci static const struct snd_device_ops ops = { 41062306a36Sopenharmony_ci .dev_disconnect = hda_tegra_dev_disconnect, 41162306a36Sopenharmony_ci .dev_free = hda_tegra_dev_free, 41262306a36Sopenharmony_ci }; 41362306a36Sopenharmony_ci struct azx *chip; 41462306a36Sopenharmony_ci int err; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci chip = &hda->chip; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci mutex_init(&chip->open_mutex); 41962306a36Sopenharmony_ci chip->card = card; 42062306a36Sopenharmony_ci chip->ops = &hda_tegra_ops; 42162306a36Sopenharmony_ci chip->driver_caps = driver_caps; 42262306a36Sopenharmony_ci chip->driver_type = driver_caps & 0xff; 42362306a36Sopenharmony_ci chip->dev_index = 0; 42462306a36Sopenharmony_ci chip->jackpoll_interval = msecs_to_jiffies(5000); 42562306a36Sopenharmony_ci INIT_LIST_HEAD(&chip->pcm_list); 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci chip->codec_probe_mask = -1; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci chip->single_cmd = false; 43062306a36Sopenharmony_ci chip->snoop = true; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci INIT_WORK(&hda->probe_work, hda_tegra_probe_work); 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci err = azx_bus_init(chip, NULL); 43562306a36Sopenharmony_ci if (err < 0) 43662306a36Sopenharmony_ci return err; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci chip->bus.core.sync_write = 0; 43962306a36Sopenharmony_ci chip->bus.core.needs_damn_long_delay = 1; 44062306a36Sopenharmony_ci chip->bus.core.aligned_mmio = 1; 44162306a36Sopenharmony_ci chip->bus.jackpoll_in_suspend = 1; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops); 44462306a36Sopenharmony_ci if (err < 0) { 44562306a36Sopenharmony_ci dev_err(card->dev, "Error creating device\n"); 44662306a36Sopenharmony_ci return err; 44762306a36Sopenharmony_ci } 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci return 0; 45062306a36Sopenharmony_ci} 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_cistatic const struct hda_tegra_soc tegra30_data = { 45362306a36Sopenharmony_ci .has_hda2codec_2x_reset = true, 45462306a36Sopenharmony_ci .has_hda2hdmi = true, 45562306a36Sopenharmony_ci}; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_cistatic const struct hda_tegra_soc tegra194_data = { 45862306a36Sopenharmony_ci .has_hda2codec_2x_reset = false, 45962306a36Sopenharmony_ci .has_hda2hdmi = true, 46062306a36Sopenharmony_ci}; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_cistatic const struct hda_tegra_soc tegra234_data = { 46362306a36Sopenharmony_ci .has_hda2codec_2x_reset = true, 46462306a36Sopenharmony_ci .has_hda2hdmi = false, 46562306a36Sopenharmony_ci}; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_cistatic const struct of_device_id hda_tegra_match[] = { 46862306a36Sopenharmony_ci { .compatible = "nvidia,tegra30-hda", .data = &tegra30_data }, 46962306a36Sopenharmony_ci { .compatible = "nvidia,tegra194-hda", .data = &tegra194_data }, 47062306a36Sopenharmony_ci { .compatible = "nvidia,tegra234-hda", .data = &tegra234_data }, 47162306a36Sopenharmony_ci {}, 47262306a36Sopenharmony_ci}; 47362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, hda_tegra_match); 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_cistatic int hda_tegra_probe(struct platform_device *pdev) 47662306a36Sopenharmony_ci{ 47762306a36Sopenharmony_ci const unsigned int driver_flags = AZX_DCAPS_CORBRP_SELF_CLEAR | 47862306a36Sopenharmony_ci AZX_DCAPS_PM_RUNTIME | 47962306a36Sopenharmony_ci AZX_DCAPS_4K_BDLE_BOUNDARY; 48062306a36Sopenharmony_ci struct snd_card *card; 48162306a36Sopenharmony_ci struct azx *chip; 48262306a36Sopenharmony_ci struct hda_tegra *hda; 48362306a36Sopenharmony_ci int err; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci hda = devm_kzalloc(&pdev->dev, sizeof(*hda), GFP_KERNEL); 48662306a36Sopenharmony_ci if (!hda) 48762306a36Sopenharmony_ci return -ENOMEM; 48862306a36Sopenharmony_ci hda->dev = &pdev->dev; 48962306a36Sopenharmony_ci chip = &hda->chip; 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci hda->soc = of_device_get_match_data(&pdev->dev); 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci err = snd_card_new(&pdev->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, 49462306a36Sopenharmony_ci THIS_MODULE, 0, &card); 49562306a36Sopenharmony_ci if (err < 0) { 49662306a36Sopenharmony_ci dev_err(&pdev->dev, "Error creating card!\n"); 49762306a36Sopenharmony_ci return err; 49862306a36Sopenharmony_ci } 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci hda->resets[hda->nresets++].id = "hda"; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci /* 50362306a36Sopenharmony_ci * "hda2hdmi" is not applicable for Tegra234. This is because the 50462306a36Sopenharmony_ci * codec is separate IP and not under display SOR partition now. 50562306a36Sopenharmony_ci */ 50662306a36Sopenharmony_ci if (hda->soc->has_hda2hdmi) 50762306a36Sopenharmony_ci hda->resets[hda->nresets++].id = "hda2hdmi"; 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci /* 51062306a36Sopenharmony_ci * "hda2codec_2x" reset is not present on Tegra194. Though DT would 51162306a36Sopenharmony_ci * be updated to reflect this, but to have backward compatibility 51262306a36Sopenharmony_ci * below is necessary. 51362306a36Sopenharmony_ci */ 51462306a36Sopenharmony_ci if (hda->soc->has_hda2codec_2x_reset) 51562306a36Sopenharmony_ci hda->resets[hda->nresets++].id = "hda2codec_2x"; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci err = devm_reset_control_bulk_get_exclusive(&pdev->dev, hda->nresets, 51862306a36Sopenharmony_ci hda->resets); 51962306a36Sopenharmony_ci if (err) 52062306a36Sopenharmony_ci goto out_free; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci hda->clocks[hda->nclocks++].id = "hda"; 52362306a36Sopenharmony_ci if (hda->soc->has_hda2hdmi) 52462306a36Sopenharmony_ci hda->clocks[hda->nclocks++].id = "hda2hdmi"; 52562306a36Sopenharmony_ci hda->clocks[hda->nclocks++].id = "hda2codec_2x"; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci err = devm_clk_bulk_get(&pdev->dev, hda->nclocks, hda->clocks); 52862306a36Sopenharmony_ci if (err < 0) 52962306a36Sopenharmony_ci goto out_free; 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci err = hda_tegra_create(card, driver_flags, hda); 53262306a36Sopenharmony_ci if (err < 0) 53362306a36Sopenharmony_ci goto out_free; 53462306a36Sopenharmony_ci card->private_data = chip; 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci dev_set_drvdata(&pdev->dev, card); 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci pm_runtime_enable(hda->dev); 53962306a36Sopenharmony_ci if (!azx_has_pm_runtime(chip)) 54062306a36Sopenharmony_ci pm_runtime_forbid(hda->dev); 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci schedule_work(&hda->probe_work); 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci return 0; 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ciout_free: 54762306a36Sopenharmony_ci snd_card_free(card); 54862306a36Sopenharmony_ci return err; 54962306a36Sopenharmony_ci} 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_cistatic void hda_tegra_probe_work(struct work_struct *work) 55262306a36Sopenharmony_ci{ 55362306a36Sopenharmony_ci struct hda_tegra *hda = container_of(work, struct hda_tegra, probe_work); 55462306a36Sopenharmony_ci struct azx *chip = &hda->chip; 55562306a36Sopenharmony_ci struct platform_device *pdev = to_platform_device(hda->dev); 55662306a36Sopenharmony_ci int err; 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci pm_runtime_get_sync(hda->dev); 55962306a36Sopenharmony_ci err = hda_tegra_first_init(chip, pdev); 56062306a36Sopenharmony_ci if (err < 0) 56162306a36Sopenharmony_ci goto out_free; 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci /* create codec instances */ 56462306a36Sopenharmony_ci err = azx_probe_codecs(chip, 8); 56562306a36Sopenharmony_ci if (err < 0) 56662306a36Sopenharmony_ci goto out_free; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci err = azx_codec_configure(chip); 56962306a36Sopenharmony_ci if (err < 0) 57062306a36Sopenharmony_ci goto out_free; 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci err = snd_card_register(chip->card); 57362306a36Sopenharmony_ci if (err < 0) 57462306a36Sopenharmony_ci goto out_free; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci chip->running = 1; 57762306a36Sopenharmony_ci snd_hda_set_power_save(&chip->bus, power_save * 1000); 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci out_free: 58062306a36Sopenharmony_ci pm_runtime_put(hda->dev); 58162306a36Sopenharmony_ci return; /* no error return from async probe */ 58262306a36Sopenharmony_ci} 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_cistatic void hda_tegra_remove(struct platform_device *pdev) 58562306a36Sopenharmony_ci{ 58662306a36Sopenharmony_ci snd_card_free(dev_get_drvdata(&pdev->dev)); 58762306a36Sopenharmony_ci pm_runtime_disable(&pdev->dev); 58862306a36Sopenharmony_ci} 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_cistatic void hda_tegra_shutdown(struct platform_device *pdev) 59162306a36Sopenharmony_ci{ 59262306a36Sopenharmony_ci struct snd_card *card = dev_get_drvdata(&pdev->dev); 59362306a36Sopenharmony_ci struct azx *chip; 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci if (!card) 59662306a36Sopenharmony_ci return; 59762306a36Sopenharmony_ci chip = card->private_data; 59862306a36Sopenharmony_ci if (chip && chip->running) 59962306a36Sopenharmony_ci azx_stop_chip(chip); 60062306a36Sopenharmony_ci} 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_cistatic struct platform_driver tegra_platform_hda = { 60362306a36Sopenharmony_ci .driver = { 60462306a36Sopenharmony_ci .name = "tegra-hda", 60562306a36Sopenharmony_ci .pm = &hda_tegra_pm, 60662306a36Sopenharmony_ci .of_match_table = hda_tegra_match, 60762306a36Sopenharmony_ci }, 60862306a36Sopenharmony_ci .probe = hda_tegra_probe, 60962306a36Sopenharmony_ci .remove_new = hda_tegra_remove, 61062306a36Sopenharmony_ci .shutdown = hda_tegra_shutdown, 61162306a36Sopenharmony_ci}; 61262306a36Sopenharmony_cimodule_platform_driver(tegra_platform_hda); 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ciMODULE_DESCRIPTION("Tegra HDA bus driver"); 61562306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 616