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