xref: /openbmc/linux/sound/pci/hda/hda_tegra.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
19952f691SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
23c320f3fSDylan Reid /*
33c320f3fSDylan Reid  *
43c320f3fSDylan Reid  * Implementation of primary ALSA driver code base for NVIDIA Tegra HDA.
53c320f3fSDylan Reid  */
63c320f3fSDylan Reid 
73c320f3fSDylan Reid #include <linux/clk.h>
83c320f3fSDylan Reid #include <linux/clocksource.h>
93c320f3fSDylan Reid #include <linux/completion.h>
103c320f3fSDylan Reid #include <linux/delay.h>
113c320f3fSDylan Reid #include <linux/dma-mapping.h>
123c320f3fSDylan Reid #include <linux/init.h>
133c320f3fSDylan Reid #include <linux/interrupt.h>
143c320f3fSDylan Reid #include <linux/io.h>
153c320f3fSDylan Reid #include <linux/kernel.h>
163c320f3fSDylan Reid #include <linux/module.h>
173c320f3fSDylan Reid #include <linux/moduleparam.h>
183c320f3fSDylan Reid #include <linux/mutex.h>
197e9f2839SRob Herring #include <linux/of.h>
207e9f2839SRob Herring #include <linux/platform_device.h>
2187f0e46eSDmitry Osipenko #include <linux/reset.h>
223c320f3fSDylan Reid #include <linux/slab.h>
233c320f3fSDylan Reid #include <linux/time.h>
24c94800a3SSameer Pujar #include <linux/string.h>
253f7e94e6SSameer Pujar #include <linux/pm_runtime.h>
263c320f3fSDylan Reid 
273c320f3fSDylan Reid #include <sound/core.h>
283c320f3fSDylan Reid #include <sound/initval.h>
293c320f3fSDylan Reid 
30be57bfffSPierre-Louis Bossart #include <sound/hda_codec.h>
313c320f3fSDylan Reid #include "hda_controller.h"
323c320f3fSDylan Reid 
333c320f3fSDylan Reid /* Defines for Nvidia Tegra HDA support */
343c320f3fSDylan Reid #define HDA_BAR0           0x8000
353c320f3fSDylan Reid 
363c320f3fSDylan Reid #define HDA_CFG_CMD        0x1004
373c320f3fSDylan Reid #define HDA_CFG_BAR0       0x1010
383c320f3fSDylan Reid 
393c320f3fSDylan Reid #define HDA_ENABLE_IO_SPACE       (1 << 0)
403c320f3fSDylan Reid #define HDA_ENABLE_MEM_SPACE      (1 << 1)
413c320f3fSDylan Reid #define HDA_ENABLE_BUS_MASTER     (1 << 2)
423c320f3fSDylan Reid #define HDA_ENABLE_SERR           (1 << 8)
433c320f3fSDylan Reid #define HDA_DISABLE_INTR          (1 << 10)
443c320f3fSDylan Reid #define HDA_BAR0_INIT_PROGRAM     0xFFFFFFFF
453c320f3fSDylan Reid #define HDA_BAR0_FINAL_PROGRAM    (1 << 14)
463c320f3fSDylan Reid 
473c320f3fSDylan Reid /* IPFS */
483c320f3fSDylan Reid #define HDA_IPFS_CONFIG           0x180
493c320f3fSDylan Reid #define HDA_IPFS_EN_FPCI          0x1
503c320f3fSDylan Reid 
513c320f3fSDylan Reid #define HDA_IPFS_FPCI_BAR0        0x80
523c320f3fSDylan Reid #define HDA_FPCI_BAR0_START       0x40
533c320f3fSDylan Reid 
543c320f3fSDylan Reid #define HDA_IPFS_INTR_MASK        0x188
553c320f3fSDylan Reid #define HDA_IPFS_EN_INTR          (1 << 16)
563c320f3fSDylan Reid 
57bb9b02a4SSameer Pujar /* FPCI */
58bb9b02a4SSameer Pujar #define FPCI_DBG_CFG_2		  0x10F4
59bb9b02a4SSameer Pujar #define FPCI_GCAP_NSDO_SHIFT	  18
60bb9b02a4SSameer Pujar #define FPCI_GCAP_NSDO_MASK	  (0x3 << FPCI_GCAP_NSDO_SHIFT)
61bb9b02a4SSameer Pujar 
623c320f3fSDylan Reid /* max number of SDs */
633c320f3fSDylan Reid #define NUM_CAPTURE_SD 1
643c320f3fSDylan Reid #define NUM_PLAYBACK_SD 1
653c320f3fSDylan Reid 
66bb9b02a4SSameer Pujar /*
67bb9b02a4SSameer Pujar  * Tegra194 does not reflect correct number of SDO lines. Below macro
68bb9b02a4SSameer Pujar  * is used to update the GCAP register to workaround the issue.
69bb9b02a4SSameer Pujar  */
70bb9b02a4SSameer Pujar #define TEGRA194_NUM_SDO_LINES	  4
71bb9b02a4SSameer Pujar 
72d278dc91SSameer Pujar struct hda_tegra_soc {
73d278dc91SSameer Pujar 	bool has_hda2codec_2x_reset;
74f43156a9SMohan Kumar 	bool has_hda2hdmi;
75d278dc91SSameer Pujar };
76d278dc91SSameer Pujar 
773c320f3fSDylan Reid struct hda_tegra {
783c320f3fSDylan Reid 	struct azx chip;
793c320f3fSDylan Reid 	struct device *dev;
80d278dc91SSameer Pujar 	struct reset_control_bulk_data resets[3];
813a465f02SDmitry Osipenko 	struct clk_bulk_data clocks[3];
82d278dc91SSameer Pujar 	unsigned int nresets;
833a465f02SDmitry Osipenko 	unsigned int nclocks;
843c320f3fSDylan Reid 	void __iomem *regs;
8583510441STakashi Iwai 	struct work_struct probe_work;
86d278dc91SSameer Pujar 	const struct hda_tegra_soc *soc;
873c320f3fSDylan Reid };
883c320f3fSDylan Reid 
8916c23952SArnd Bergmann #ifdef CONFIG_PM
903c320f3fSDylan Reid static int power_save = CONFIG_SND_HDA_POWER_SAVE_DEFAULT;
913c320f3fSDylan Reid module_param(power_save, bint, 0644);
923c320f3fSDylan Reid MODULE_PARM_DESC(power_save,
933c320f3fSDylan Reid 		 "Automatic power-saving timeout (in seconds, 0 = disable).");
9416c23952SArnd Bergmann #else
95bb573928STakashi Iwai #define power_save	0
9616c23952SArnd Bergmann #endif
973c320f3fSDylan Reid 
98193c7e14STakashi Iwai static const struct hda_controller_ops hda_tegra_ops; /* nothing special */
993c320f3fSDylan Reid 
hda_tegra_init(struct hda_tegra * hda)1003c320f3fSDylan Reid static void hda_tegra_init(struct hda_tegra *hda)
1013c320f3fSDylan Reid {
1023c320f3fSDylan Reid 	u32 v;
1033c320f3fSDylan Reid 
1043c320f3fSDylan Reid 	/* Enable PCI access */
1053c320f3fSDylan Reid 	v = readl(hda->regs + HDA_IPFS_CONFIG);
1063c320f3fSDylan Reid 	v |= HDA_IPFS_EN_FPCI;
1073c320f3fSDylan Reid 	writel(v, hda->regs + HDA_IPFS_CONFIG);
1083c320f3fSDylan Reid 
1093c320f3fSDylan Reid 	/* Enable MEM/IO space and bus master */
1103c320f3fSDylan Reid 	v = readl(hda->regs + HDA_CFG_CMD);
1113c320f3fSDylan Reid 	v &= ~HDA_DISABLE_INTR;
1123c320f3fSDylan Reid 	v |= HDA_ENABLE_MEM_SPACE | HDA_ENABLE_IO_SPACE |
1133c320f3fSDylan Reid 		HDA_ENABLE_BUS_MASTER | HDA_ENABLE_SERR;
1143c320f3fSDylan Reid 	writel(v, hda->regs + HDA_CFG_CMD);
1153c320f3fSDylan Reid 
1163c320f3fSDylan Reid 	writel(HDA_BAR0_INIT_PROGRAM, hda->regs + HDA_CFG_BAR0);
1173c320f3fSDylan Reid 	writel(HDA_BAR0_FINAL_PROGRAM, hda->regs + HDA_CFG_BAR0);
1183c320f3fSDylan Reid 	writel(HDA_FPCI_BAR0_START, hda->regs + HDA_IPFS_FPCI_BAR0);
1193c320f3fSDylan Reid 
1203c320f3fSDylan Reid 	v = readl(hda->regs + HDA_IPFS_INTR_MASK);
1213c320f3fSDylan Reid 	v |= HDA_IPFS_EN_INTR;
1223c320f3fSDylan Reid 	writel(v, hda->regs + HDA_IPFS_INTR_MASK);
1233c320f3fSDylan Reid }
1243c320f3fSDylan Reid 
1253c320f3fSDylan Reid /*
1263c320f3fSDylan Reid  * power management
1273c320f3fSDylan Reid  */
hda_tegra_suspend(struct device * dev)12874729469SArnd Bergmann static int __maybe_unused hda_tegra_suspend(struct device *dev)
1293c320f3fSDylan Reid {
1303c320f3fSDylan Reid 	struct snd_card *card = dev_get_drvdata(dev);
131707e0759SSameer Pujar 	int rc;
1323c320f3fSDylan Reid 
133707e0759SSameer Pujar 	rc = pm_runtime_force_suspend(dev);
134707e0759SSameer Pujar 	if (rc < 0)
135707e0759SSameer Pujar 		return rc;
1363c320f3fSDylan Reid 	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
1373c320f3fSDylan Reid 
1383c320f3fSDylan Reid 	return 0;
1393c320f3fSDylan Reid }
1403c320f3fSDylan Reid 
hda_tegra_resume(struct device * dev)14174729469SArnd Bergmann static int __maybe_unused hda_tegra_resume(struct device *dev)
1423c320f3fSDylan Reid {
1433c320f3fSDylan Reid 	struct snd_card *card = dev_get_drvdata(dev);
144707e0759SSameer Pujar 	int rc;
1453c320f3fSDylan Reid 
146707e0759SSameer Pujar 	rc = pm_runtime_force_resume(dev);
147707e0759SSameer Pujar 	if (rc < 0)
148707e0759SSameer Pujar 		return rc;
1493c320f3fSDylan Reid 	snd_power_change_state(card, SNDRV_CTL_POWER_D0);
1503c320f3fSDylan Reid 
1513c320f3fSDylan Reid 	return 0;
1523c320f3fSDylan Reid }
1533c320f3fSDylan Reid 
hda_tegra_runtime_suspend(struct device * dev)15474729469SArnd Bergmann static int __maybe_unused hda_tegra_runtime_suspend(struct device *dev)
155f2974aa2SSameer Pujar {
156707e0759SSameer Pujar 	struct snd_card *card = dev_get_drvdata(dev);
157707e0759SSameer Pujar 	struct azx *chip = card->private_data;
158707e0759SSameer Pujar 	struct hda_tegra *hda = container_of(chip, struct hda_tegra, chip);
159707e0759SSameer Pujar 
160707e0759SSameer Pujar 	if (chip && chip->running) {
16123d63a31SMohan Kumar 		/* enable controller wake up event */
16223d63a31SMohan Kumar 		azx_writew(chip, WAKEEN, azx_readw(chip, WAKEEN) |
16323d63a31SMohan Kumar 			   STATESTS_INT_MASK);
16423d63a31SMohan Kumar 
165707e0759SSameer Pujar 		azx_stop_chip(chip);
166707e0759SSameer Pujar 		azx_enter_link_reset(chip);
167707e0759SSameer Pujar 	}
1683a465f02SDmitry Osipenko 	clk_bulk_disable_unprepare(hda->nclocks, hda->clocks);
169707e0759SSameer Pujar 
170f2974aa2SSameer Pujar 	return 0;
171f2974aa2SSameer Pujar }
172f2974aa2SSameer Pujar 
hda_tegra_runtime_resume(struct device * dev)17374729469SArnd Bergmann static int __maybe_unused hda_tegra_runtime_resume(struct device *dev)
174f2974aa2SSameer Pujar {
175707e0759SSameer Pujar 	struct snd_card *card = dev_get_drvdata(dev);
176707e0759SSameer Pujar 	struct azx *chip = card->private_data;
177707e0759SSameer Pujar 	struct hda_tegra *hda = container_of(chip, struct hda_tegra, chip);
178707e0759SSameer Pujar 	int rc;
179707e0759SSameer Pujar 
18087f0e46eSDmitry Osipenko 	if (!chip->running) {
181d278dc91SSameer Pujar 		rc = reset_control_bulk_assert(hda->nresets, hda->resets);
18287f0e46eSDmitry Osipenko 		if (rc)
18387f0e46eSDmitry Osipenko 			return rc;
18487f0e46eSDmitry Osipenko 	}
18587f0e46eSDmitry Osipenko 
1863a465f02SDmitry Osipenko 	rc = clk_bulk_prepare_enable(hda->nclocks, hda->clocks);
187707e0759SSameer Pujar 	if (rc != 0)
188707e0759SSameer Pujar 		return rc;
1896755568aSDmitry Osipenko 	if (chip->running) {
190707e0759SSameer Pujar 		hda_tegra_init(hda);
191707e0759SSameer Pujar 		azx_init_chip(chip, 1);
19223d63a31SMohan Kumar 		/* disable controller wake up event*/
19323d63a31SMohan Kumar 		azx_writew(chip, WAKEEN, azx_readw(chip, WAKEEN) &
19423d63a31SMohan Kumar 			   ~STATESTS_INT_MASK);
19587f0e46eSDmitry Osipenko 	} else {
19687f0e46eSDmitry Osipenko 		usleep_range(10, 100);
19787f0e46eSDmitry Osipenko 
198d278dc91SSameer Pujar 		rc = reset_control_bulk_deassert(hda->nresets, hda->resets);
19987f0e46eSDmitry Osipenko 		if (rc)
20087f0e46eSDmitry Osipenko 			return rc;
201707e0759SSameer Pujar 	}
202707e0759SSameer Pujar 
203f2974aa2SSameer Pujar 	return 0;
204f2974aa2SSameer Pujar }
205f2974aa2SSameer Pujar 
2063c320f3fSDylan Reid static const struct dev_pm_ops hda_tegra_pm = {
2073c320f3fSDylan Reid 	SET_SYSTEM_SLEEP_PM_OPS(hda_tegra_suspend, hda_tegra_resume)
208f2974aa2SSameer Pujar 	SET_RUNTIME_PM_OPS(hda_tegra_runtime_suspend,
209f2974aa2SSameer Pujar 			   hda_tegra_runtime_resume,
210f2974aa2SSameer Pujar 			   NULL)
2113c320f3fSDylan Reid };
2123c320f3fSDylan Reid 
hda_tegra_dev_disconnect(struct snd_device * device)213a41d1224STakashi Iwai static int hda_tegra_dev_disconnect(struct snd_device *device)
214a41d1224STakashi Iwai {
215a41d1224STakashi Iwai 	struct azx *chip = device->device_data;
216a41d1224STakashi Iwai 
217a41d1224STakashi Iwai 	chip->bus.shutdown = 1;
218a41d1224STakashi Iwai 	return 0;
219a41d1224STakashi Iwai }
220a41d1224STakashi Iwai 
2213c320f3fSDylan Reid /*
2223c320f3fSDylan Reid  * destructor
2233c320f3fSDylan Reid  */
hda_tegra_dev_free(struct snd_device * device)2243c320f3fSDylan Reid static int hda_tegra_dev_free(struct snd_device *device)
2253c320f3fSDylan Reid {
2263c320f3fSDylan Reid 	struct azx *chip = device->device_data;
22783510441STakashi Iwai 	struct hda_tegra *hda = container_of(chip, struct hda_tegra, chip);
2283c320f3fSDylan Reid 
22983510441STakashi Iwai 	cancel_work_sync(&hda->probe_work);
230a41d1224STakashi Iwai 	if (azx_bus(chip)->chip_init) {
2317833c3f8STakashi Iwai 		azx_stop_all_streams(chip);
2323c320f3fSDylan Reid 		azx_stop_chip(chip);
2333c320f3fSDylan Reid 	}
2343c320f3fSDylan Reid 
2353c320f3fSDylan Reid 	azx_free_stream_pages(chip);
236a41d1224STakashi Iwai 	azx_free_streams(chip);
2374cfe99c7STakashi Iwai 	snd_hdac_bus_exit(azx_bus(chip));
2383c320f3fSDylan Reid 
2393c320f3fSDylan Reid 	return 0;
2403c320f3fSDylan Reid }
2413c320f3fSDylan Reid 
hda_tegra_init_chip(struct azx * chip,struct platform_device * pdev)2423c320f3fSDylan Reid static int hda_tegra_init_chip(struct azx *chip, struct platform_device *pdev)
2433c320f3fSDylan Reid {
2443c320f3fSDylan Reid 	struct hda_tegra *hda = container_of(chip, struct hda_tegra, chip);
245a41d1224STakashi Iwai 	struct hdac_bus *bus = azx_bus(chip);
2463c320f3fSDylan Reid 	struct resource *res;
2473c320f3fSDylan Reid 
24801893553SYang Yingliang 	hda->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
24993ceaa30SEliot Blennerhassett 	if (IS_ERR(hda->regs))
25093ceaa30SEliot Blennerhassett 		return PTR_ERR(hda->regs);
2513c320f3fSDylan Reid 
252a41d1224STakashi Iwai 	bus->remap_addr = hda->regs + HDA_BAR0;
253a41d1224STakashi Iwai 	bus->addr = res->start + HDA_BAR0;
2543c320f3fSDylan Reid 
2553c320f3fSDylan Reid 	hda_tegra_init(hda);
2563c320f3fSDylan Reid 
2573c320f3fSDylan Reid 	return 0;
2583c320f3fSDylan Reid }
2593c320f3fSDylan Reid 
hda_tegra_first_init(struct azx * chip,struct platform_device * pdev)2603c320f3fSDylan Reid static int hda_tegra_first_init(struct azx *chip, struct platform_device *pdev)
2613c320f3fSDylan Reid {
262bb9b02a4SSameer Pujar 	struct hda_tegra *hda = container_of(chip, struct hda_tegra, chip);
263a41d1224STakashi Iwai 	struct hdac_bus *bus = azx_bus(chip);
2643c320f3fSDylan Reid 	struct snd_card *card = chip->card;
2653c320f3fSDylan Reid 	int err;
2663c320f3fSDylan Reid 	unsigned short gcap;
2673c320f3fSDylan Reid 	int irq_id = platform_get_irq(pdev, 0);
268c0bde003SSameer Pujar 	const char *sname, *drv_name = "tegra-hda";
269c0bde003SSameer Pujar 	struct device_node *np = pdev->dev.of_node;
2703c320f3fSDylan Reid 
2718c132124SJiajun Cao 	if (irq_id < 0)
2728c132124SJiajun Cao 		return irq_id;
2738c132124SJiajun Cao 
2743c320f3fSDylan Reid 	err = hda_tegra_init_chip(chip, pdev);
2753c320f3fSDylan Reid 	if (err)
2763c320f3fSDylan Reid 		return err;
2773c320f3fSDylan Reid 
2783c320f3fSDylan Reid 	err = devm_request_irq(chip->card->dev, irq_id, azx_interrupt,
2793c320f3fSDylan Reid 			     IRQF_SHARED, KBUILD_MODNAME, chip);
2803c320f3fSDylan Reid 	if (err) {
2813c320f3fSDylan Reid 		dev_err(chip->card->dev,
2823c320f3fSDylan Reid 			"unable to request IRQ %d, disabling device\n",
2833c320f3fSDylan Reid 			irq_id);
2843c320f3fSDylan Reid 		return err;
2853c320f3fSDylan Reid 	}
286a41d1224STakashi Iwai 	bus->irq = irq_id;
287ed4d0a4aSMohan Kumar 	bus->dma_stop_delay = 100;
288f36da940STakashi Iwai 	card->sync_irq = bus->irq;
2893c320f3fSDylan Reid 
290bb9b02a4SSameer Pujar 	/*
291bb9b02a4SSameer Pujar 	 * Tegra194 has 4 SDO lines and the STRIPE can be used to
292bb9b02a4SSameer Pujar 	 * indicate how many of the SDO lines the stream should be
293bb9b02a4SSameer Pujar 	 * striped. But GCAP register does not reflect the true
294bb9b02a4SSameer Pujar 	 * capability of HW. Below workaround helps to fix this.
295bb9b02a4SSameer Pujar 	 *
296bb9b02a4SSameer Pujar 	 * GCAP_NSDO is bits 19:18 in T_AZA_DBG_CFG_2,
297bb9b02a4SSameer Pujar 	 * 0 for 1 SDO, 1 for 2 SDO, 2 for 4 SDO lines.
298bb9b02a4SSameer Pujar 	 */
299bb9b02a4SSameer Pujar 	if (of_device_is_compatible(np, "nvidia,tegra194-hda")) {
300bb9b02a4SSameer Pujar 		u32 val;
301bb9b02a4SSameer Pujar 
302bb9b02a4SSameer Pujar 		dev_info(card->dev, "Override SDO lines to %u\n",
303bb9b02a4SSameer Pujar 			 TEGRA194_NUM_SDO_LINES);
304bb9b02a4SSameer Pujar 
305bb9b02a4SSameer Pujar 		val = readl(hda->regs + FPCI_DBG_CFG_2) & ~FPCI_GCAP_NSDO_MASK;
306bb9b02a4SSameer Pujar 		val |= (TEGRA194_NUM_SDO_LINES >> 1) << FPCI_GCAP_NSDO_SHIFT;
307bb9b02a4SSameer Pujar 		writel(val, hda->regs + FPCI_DBG_CFG_2);
308bb9b02a4SSameer Pujar 	}
309bb9b02a4SSameer Pujar 
3103c320f3fSDylan Reid 	gcap = azx_readw(chip, GCAP);
3113c320f3fSDylan Reid 	dev_dbg(card->dev, "chipset global capabilities = 0x%x\n", gcap);
3123c320f3fSDylan Reid 
3136c17e9ddSMohan Kumar 	chip->align_buffer_size = 1;
3146c17e9ddSMohan Kumar 
3153c320f3fSDylan Reid 	/* read number of streams from GCAP register instead of using
3163c320f3fSDylan Reid 	 * hardcoded value
3173c320f3fSDylan Reid 	 */
3183c320f3fSDylan Reid 	chip->capture_streams = (gcap >> 8) & 0x0f;
319b58d511dSMohan Kumar 
320b58d511dSMohan Kumar 	/* The GCAP register on Tegra234 implies no Input Streams(ISS) support,
321b58d511dSMohan Kumar 	 * but the HW output stream descriptor programming should start with
322b58d511dSMohan Kumar 	 * offset 0x20*4 from base stream descriptor address. This will be a
323b58d511dSMohan Kumar 	 * problem while calculating the offset for output stream descriptor
324b58d511dSMohan Kumar 	 * which will be considering input stream also. So here output stream
325b58d511dSMohan Kumar 	 * starts with offset 0 which is wrong as HW register for output stream
326b58d511dSMohan Kumar 	 * offset starts with 4.
327b58d511dSMohan Kumar 	 */
328b58d511dSMohan Kumar 	if (of_device_is_compatible(np, "nvidia,tegra234-hda"))
329b58d511dSMohan Kumar 		chip->capture_streams = 4;
330b58d511dSMohan Kumar 
3313c320f3fSDylan Reid 	chip->playback_streams = (gcap >> 12) & 0x0f;
3323c320f3fSDylan Reid 	if (!chip->playback_streams && !chip->capture_streams) {
3333c320f3fSDylan Reid 		/* gcap didn't give any info, switching to old method */
3343c320f3fSDylan Reid 		chip->playback_streams = NUM_PLAYBACK_SD;
3353c320f3fSDylan Reid 		chip->capture_streams = NUM_CAPTURE_SD;
3363c320f3fSDylan Reid 	}
3373c320f3fSDylan Reid 	chip->capture_index_offset = 0;
3383c320f3fSDylan Reid 	chip->playback_index_offset = chip->capture_streams;
3393c320f3fSDylan Reid 	chip->num_streams = chip->playback_streams + chip->capture_streams;
3403c320f3fSDylan Reid 
341a41d1224STakashi Iwai 	/* initialize streams */
342a41d1224STakashi Iwai 	err = azx_init_streams(chip);
3436a464a4cSThierry Reding 	if (err < 0) {
3446a464a4cSThierry Reding 		dev_err(card->dev, "failed to initialize streams: %d\n", err);
3453c320f3fSDylan Reid 		return err;
3466a464a4cSThierry Reding 	}
3473c320f3fSDylan Reid 
348a41d1224STakashi Iwai 	err = azx_alloc_stream_pages(chip);
3496a464a4cSThierry Reding 	if (err < 0) {
3506a464a4cSThierry Reding 		dev_err(card->dev, "failed to allocate stream pages: %d\n",
3516a464a4cSThierry Reding 			err);
352a41d1224STakashi Iwai 		return err;
3536a464a4cSThierry Reding 	}
3543c320f3fSDylan Reid 
3553c320f3fSDylan Reid 	/* initialize chip */
3563c320f3fSDylan Reid 	azx_init_chip(chip, 1);
3573c320f3fSDylan Reid 
35860019d8cSSameer Pujar 	/*
35960019d8cSSameer Pujar 	 * Playback (for 44.1K/48K, 2-channel, 16-bps) fails with
36060019d8cSSameer Pujar 	 * 4 SDO lines due to legacy design limitation. Following
36160019d8cSSameer Pujar 	 * is, from HD Audio Specification (Revision 1.0a), used to
36260019d8cSSameer Pujar 	 * control striping of the stream across multiple SDO lines
36360019d8cSSameer Pujar 	 * for sample rates <= 48K.
36460019d8cSSameer Pujar 	 *
36560019d8cSSameer Pujar 	 * { ((num_channels * bits_per_sample) / number of SDOs) >= 8 }
36660019d8cSSameer Pujar 	 *
36760019d8cSSameer Pujar 	 * Due to legacy design issue it is recommended that above
36860019d8cSSameer Pujar 	 * ratio must be greater than 8. Since number of SDO lines is
36960019d8cSSameer Pujar 	 * in powers of 2, next available ratio is 16 which can be
37060019d8cSSameer Pujar 	 * used as a limiting factor here.
37160019d8cSSameer Pujar 	 */
372615d4354SPeter Geis 	if (of_device_is_compatible(np, "nvidia,tegra30-hda"))
37360019d8cSSameer Pujar 		chip->bus.core.sdo_limit = 16;
37460019d8cSSameer Pujar 
3753c320f3fSDylan Reid 	/* codec detection */
376a41d1224STakashi Iwai 	if (!bus->codec_mask) {
3773c320f3fSDylan Reid 		dev_err(card->dev, "no codecs found!\n");
3783c320f3fSDylan Reid 		return -ENODEV;
3793c320f3fSDylan Reid 	}
3803c320f3fSDylan Reid 
381c94800a3SSameer Pujar 	/* driver name */
382*3d28c466SJustin Stitt 	strscpy(card->driver, drv_name, sizeof(card->driver));
383c94800a3SSameer Pujar 	/* shortname for card */
384c0bde003SSameer Pujar 	sname = of_get_property(np, "nvidia,model", NULL);
385c0bde003SSameer Pujar 	if (!sname)
386c0bde003SSameer Pujar 		sname = drv_name;
387c94800a3SSameer Pujar 	if (strlen(sname) > sizeof(card->shortname))
388c94800a3SSameer Pujar 		dev_info(card->dev, "truncating shortname for card\n");
389*3d28c466SJustin Stitt 	strscpy(card->shortname, sname, sizeof(card->shortname));
390c94800a3SSameer Pujar 
391c94800a3SSameer Pujar 	/* longname for card */
3923c320f3fSDylan Reid 	snprintf(card->longname, sizeof(card->longname),
3933c320f3fSDylan Reid 		 "%s at 0x%lx irq %i",
394a41d1224STakashi Iwai 		 card->shortname, bus->addr, bus->irq);
3953c320f3fSDylan Reid 
3963c320f3fSDylan Reid 	return 0;
3973c320f3fSDylan Reid }
3983c320f3fSDylan Reid 
3993c320f3fSDylan Reid /*
4003c320f3fSDylan Reid  * constructor
4013c320f3fSDylan Reid  */
40283510441STakashi Iwai 
40383510441STakashi Iwai static void hda_tegra_probe_work(struct work_struct *work);
40483510441STakashi Iwai 
hda_tegra_create(struct snd_card * card,unsigned int driver_caps,struct hda_tegra * hda)4053c320f3fSDylan Reid static int hda_tegra_create(struct snd_card *card,
4063c320f3fSDylan Reid 			    unsigned int driver_caps,
4073c320f3fSDylan Reid 			    struct hda_tegra *hda)
4083c320f3fSDylan Reid {
40941f394a8STakashi Iwai 	static const struct snd_device_ops ops = {
410a41d1224STakashi Iwai 		.dev_disconnect = hda_tegra_dev_disconnect,
4113c320f3fSDylan Reid 		.dev_free = hda_tegra_dev_free,
4123c320f3fSDylan Reid 	};
4133c320f3fSDylan Reid 	struct azx *chip;
4143c320f3fSDylan Reid 	int err;
4153c320f3fSDylan Reid 
4163c320f3fSDylan Reid 	chip = &hda->chip;
4173c320f3fSDylan Reid 
4183c320f3fSDylan Reid 	mutex_init(&chip->open_mutex);
4193c320f3fSDylan Reid 	chip->card = card;
420a43ff5baSTakashi Iwai 	chip->ops = &hda_tegra_ops;
4213c320f3fSDylan Reid 	chip->driver_caps = driver_caps;
4223c320f3fSDylan Reid 	chip->driver_type = driver_caps & 0xff;
4233c320f3fSDylan Reid 	chip->dev_index = 0;
4248f06bd1fSMohan Kumar 	chip->jackpoll_interval = msecs_to_jiffies(5000);
4253c320f3fSDylan Reid 	INIT_LIST_HEAD(&chip->pcm_list);
4263c320f3fSDylan Reid 
4273c320f3fSDylan Reid 	chip->codec_probe_mask = -1;
4283c320f3fSDylan Reid 
4293c320f3fSDylan Reid 	chip->single_cmd = false;
4303c320f3fSDylan Reid 	chip->snoop = true;
4313c320f3fSDylan Reid 
43283510441STakashi Iwai 	INIT_WORK(&hda->probe_work, hda_tegra_probe_work);
43383510441STakashi Iwai 
43419abfefdSTakashi Iwai 	err = azx_bus_init(chip, NULL);
4353b90f407SThierry Reding 	if (err < 0)
4363b90f407SThierry Reding 		return err;
4373b90f407SThierry Reding 
438ee85a360SJon Hunter 	chip->bus.core.sync_write = 0;
4395f2cb361STakashi Iwai 	chip->bus.core.needs_damn_long_delay = 1;
4404d024fe8STakashi Iwai 	chip->bus.core.aligned_mmio = 1;
4418f06bd1fSMohan Kumar 	chip->bus.jackpoll_in_suspend = 1;
4427d9a1808STakashi Iwai 
4433c320f3fSDylan Reid 	err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
4443c320f3fSDylan Reid 	if (err < 0) {
4453c320f3fSDylan Reid 		dev_err(card->dev, "Error creating device\n");
4463c320f3fSDylan Reid 		return err;
4473c320f3fSDylan Reid 	}
4483c320f3fSDylan Reid 
4493c320f3fSDylan Reid 	return 0;
4503c320f3fSDylan Reid }
4513c320f3fSDylan Reid 
452d278dc91SSameer Pujar static const struct hda_tegra_soc tegra30_data = {
453d278dc91SSameer Pujar 	.has_hda2codec_2x_reset = true,
454f43156a9SMohan Kumar 	.has_hda2hdmi = true,
455d278dc91SSameer Pujar };
456d278dc91SSameer Pujar 
457d278dc91SSameer Pujar static const struct hda_tegra_soc tegra194_data = {
458d278dc91SSameer Pujar 	.has_hda2codec_2x_reset = false,
459f43156a9SMohan Kumar 	.has_hda2hdmi = true,
460f43156a9SMohan Kumar };
461f43156a9SMohan Kumar 
462f43156a9SMohan Kumar static const struct hda_tegra_soc tegra234_data = {
463f43156a9SMohan Kumar 	.has_hda2codec_2x_reset = true,
464f43156a9SMohan Kumar 	.has_hda2hdmi = false,
465d278dc91SSameer Pujar };
466d278dc91SSameer Pujar 
4673c320f3fSDylan Reid static const struct of_device_id hda_tegra_match[] = {
468d278dc91SSameer Pujar 	{ .compatible = "nvidia,tegra30-hda", .data = &tegra30_data },
469d278dc91SSameer Pujar 	{ .compatible = "nvidia,tegra194-hda", .data = &tegra194_data },
470f43156a9SMohan Kumar 	{ .compatible = "nvidia,tegra234-hda", .data = &tegra234_data },
4713c320f3fSDylan Reid 	{},
4723c320f3fSDylan Reid };
473f73387cbSDylan Reid MODULE_DEVICE_TABLE(of, hda_tegra_match);
4743c320f3fSDylan Reid 
hda_tegra_probe(struct platform_device * pdev)4753c320f3fSDylan Reid static int hda_tegra_probe(struct platform_device *pdev)
4763c320f3fSDylan Reid {
4779935d55bSSameer Pujar 	const unsigned int driver_flags = AZX_DCAPS_CORBRP_SELF_CLEAR |
4788d44e604SMohan Kumar 					  AZX_DCAPS_PM_RUNTIME |
4798d44e604SMohan Kumar 					  AZX_DCAPS_4K_BDLE_BOUNDARY;
4803c320f3fSDylan Reid 	struct snd_card *card;
4813c320f3fSDylan Reid 	struct azx *chip;
4823c320f3fSDylan Reid 	struct hda_tegra *hda;
4833c320f3fSDylan Reid 	int err;
4843c320f3fSDylan Reid 
4853c320f3fSDylan Reid 	hda = devm_kzalloc(&pdev->dev, sizeof(*hda), GFP_KERNEL);
4863c320f3fSDylan Reid 	if (!hda)
4873c320f3fSDylan Reid 		return -ENOMEM;
4883c320f3fSDylan Reid 	hda->dev = &pdev->dev;
4893c320f3fSDylan Reid 	chip = &hda->chip;
4903c320f3fSDylan Reid 
491d278dc91SSameer Pujar 	hda->soc = of_device_get_match_data(&pdev->dev);
492d278dc91SSameer Pujar 
4933c320f3fSDylan Reid 	err = snd_card_new(&pdev->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
4943c320f3fSDylan Reid 			   THIS_MODULE, 0, &card);
4953c320f3fSDylan Reid 	if (err < 0) {
4963c320f3fSDylan Reid 		dev_err(&pdev->dev, "Error creating card!\n");
4973c320f3fSDylan Reid 		return err;
4983c320f3fSDylan Reid 	}
4993c320f3fSDylan Reid 
500d278dc91SSameer Pujar 	hda->resets[hda->nresets++].id = "hda";
501f43156a9SMohan Kumar 
502f43156a9SMohan Kumar 	/*
503f43156a9SMohan Kumar 	 * "hda2hdmi" is not applicable for Tegra234. This is because the
504f43156a9SMohan Kumar 	 * codec is separate IP and not under display SOR partition now.
505f43156a9SMohan Kumar 	 */
506f43156a9SMohan Kumar 	if (hda->soc->has_hda2hdmi)
507d278dc91SSameer Pujar 		hda->resets[hda->nresets++].id = "hda2hdmi";
508f43156a9SMohan Kumar 
509d278dc91SSameer Pujar 	/*
510d278dc91SSameer Pujar 	 * "hda2codec_2x" reset is not present on Tegra194. Though DT would
511d278dc91SSameer Pujar 	 * be updated to reflect this, but to have backward compatibility
512d278dc91SSameer Pujar 	 * below is necessary.
513d278dc91SSameer Pujar 	 */
514d278dc91SSameer Pujar 	if (hda->soc->has_hda2codec_2x_reset)
515d278dc91SSameer Pujar 		hda->resets[hda->nresets++].id = "hda2codec_2x";
516d278dc91SSameer Pujar 
517d278dc91SSameer Pujar 	err = devm_reset_control_bulk_get_exclusive(&pdev->dev, hda->nresets,
518d278dc91SSameer Pujar 						    hda->resets);
519d278dc91SSameer Pujar 	if (err)
52087f0e46eSDmitry Osipenko 		goto out_free;
52187f0e46eSDmitry Osipenko 
5223a465f02SDmitry Osipenko 	hda->clocks[hda->nclocks++].id = "hda";
523f43156a9SMohan Kumar 	if (hda->soc->has_hda2hdmi)
5243a465f02SDmitry Osipenko 		hda->clocks[hda->nclocks++].id = "hda2hdmi";
5253a465f02SDmitry Osipenko 	hda->clocks[hda->nclocks++].id = "hda2codec_2x";
5263a465f02SDmitry Osipenko 
5273a465f02SDmitry Osipenko 	err = devm_clk_bulk_get(&pdev->dev, hda->nclocks, hda->clocks);
52865af2122SSameer Pujar 	if (err < 0)
52965af2122SSameer Pujar 		goto out_free;
53065af2122SSameer Pujar 
531a43ff5baSTakashi Iwai 	err = hda_tegra_create(card, driver_flags, hda);
5323c320f3fSDylan Reid 	if (err < 0)
5333c320f3fSDylan Reid 		goto out_free;
5343c320f3fSDylan Reid 	card->private_data = chip;
5353c320f3fSDylan Reid 
5363c320f3fSDylan Reid 	dev_set_drvdata(&pdev->dev, card);
5373f7e94e6SSameer Pujar 
5383f7e94e6SSameer Pujar 	pm_runtime_enable(hda->dev);
5393f7e94e6SSameer Pujar 	if (!azx_has_pm_runtime(chip))
5403f7e94e6SSameer Pujar 		pm_runtime_forbid(hda->dev);
5413f7e94e6SSameer Pujar 
54283510441STakashi Iwai 	schedule_work(&hda->probe_work);
54383510441STakashi Iwai 
54483510441STakashi Iwai 	return 0;
54583510441STakashi Iwai 
54683510441STakashi Iwai out_free:
54783510441STakashi Iwai 	snd_card_free(card);
54883510441STakashi Iwai 	return err;
54983510441STakashi Iwai }
55083510441STakashi Iwai 
hda_tegra_probe_work(struct work_struct * work)55183510441STakashi Iwai static void hda_tegra_probe_work(struct work_struct *work)
55283510441STakashi Iwai {
55383510441STakashi Iwai 	struct hda_tegra *hda = container_of(work, struct hda_tegra, probe_work);
55483510441STakashi Iwai 	struct azx *chip = &hda->chip;
55583510441STakashi Iwai 	struct platform_device *pdev = to_platform_device(hda->dev);
55683510441STakashi Iwai 	int err;
5573c320f3fSDylan Reid 
5583f7e94e6SSameer Pujar 	pm_runtime_get_sync(hda->dev);
5593c320f3fSDylan Reid 	err = hda_tegra_first_init(chip, pdev);
5603c320f3fSDylan Reid 	if (err < 0)
5613c320f3fSDylan Reid 		goto out_free;
5623c320f3fSDylan Reid 
5633c320f3fSDylan Reid 	/* create codec instances */
564350355e3SThierry Reding 	err = azx_probe_codecs(chip, 8);
5653c320f3fSDylan Reid 	if (err < 0)
5663c320f3fSDylan Reid 		goto out_free;
5673c320f3fSDylan Reid 
5683c320f3fSDylan Reid 	err = azx_codec_configure(chip);
5693c320f3fSDylan Reid 	if (err < 0)
5703c320f3fSDylan Reid 		goto out_free;
5713c320f3fSDylan Reid 
5723c320f3fSDylan Reid 	err = snd_card_register(chip->card);
5733c320f3fSDylan Reid 	if (err < 0)
5743c320f3fSDylan Reid 		goto out_free;
5753c320f3fSDylan Reid 
5763c320f3fSDylan Reid 	chip->running = 1;
577a41d1224STakashi Iwai 	snd_hda_set_power_save(&chip->bus, power_save * 1000);
5783c320f3fSDylan Reid 
5793c320f3fSDylan Reid  out_free:
5803f7e94e6SSameer Pujar 	pm_runtime_put(hda->dev);
58183510441STakashi Iwai 	return; /* no error return from async probe */
5823c320f3fSDylan Reid }
5833c320f3fSDylan Reid 
hda_tegra_remove(struct platform_device * pdev)584d8a3441bSUwe Kleine-König static void hda_tegra_remove(struct platform_device *pdev)
5853c320f3fSDylan Reid {
58625a5a77aSUwe Kleine-König 	snd_card_free(dev_get_drvdata(&pdev->dev));
5873f7e94e6SSameer Pujar 	pm_runtime_disable(&pdev->dev);
5883c320f3fSDylan Reid }
5893c320f3fSDylan Reid 
hda_tegra_shutdown(struct platform_device * pdev)590b2a0bafaSTakashi Iwai static void hda_tegra_shutdown(struct platform_device *pdev)
591b2a0bafaSTakashi Iwai {
592b2a0bafaSTakashi Iwai 	struct snd_card *card = dev_get_drvdata(&pdev->dev);
593b2a0bafaSTakashi Iwai 	struct azx *chip;
594b2a0bafaSTakashi Iwai 
595b2a0bafaSTakashi Iwai 	if (!card)
596b2a0bafaSTakashi Iwai 		return;
597b2a0bafaSTakashi Iwai 	chip = card->private_data;
598b2a0bafaSTakashi Iwai 	if (chip && chip->running)
599b2a0bafaSTakashi Iwai 		azx_stop_chip(chip);
600b2a0bafaSTakashi Iwai }
601b2a0bafaSTakashi Iwai 
6023c320f3fSDylan Reid static struct platform_driver tegra_platform_hda = {
6033c320f3fSDylan Reid 	.driver = {
6043c320f3fSDylan Reid 		.name = "tegra-hda",
6053c320f3fSDylan Reid 		.pm = &hda_tegra_pm,
6063c320f3fSDylan Reid 		.of_match_table = hda_tegra_match,
6073c320f3fSDylan Reid 	},
6083c320f3fSDylan Reid 	.probe = hda_tegra_probe,
609d8a3441bSUwe Kleine-König 	.remove_new = hda_tegra_remove,
610b2a0bafaSTakashi Iwai 	.shutdown = hda_tegra_shutdown,
6113c320f3fSDylan Reid };
6123c320f3fSDylan Reid module_platform_driver(tegra_platform_hda);
6133c320f3fSDylan Reid 
6143c320f3fSDylan Reid MODULE_DESCRIPTION("Tegra HDA bus driver");
6153c320f3fSDylan Reid MODULE_LICENSE("GPL v2");
616