1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Intel HDA audio (Azalia) for ivybridge
4 *
5 * Originally from coreboot file bd82x6x/azalia.c
6 *
7 * Copyright (C) 2008 Advanced Micro Devices, Inc.
8 * Copyright (C) 2008-2009 coresystems GmbH
9 * Copyright (C) 2011 The ChromiumOS Authors.
10 * Copyright 2018 Google LLC
11 */
12
13#define LOG_CATEGORY UCLASS_SOUND
14
15#include <common.h>
16#include <dm.h>
17#include <hda_codec.h>
18#include <log.h>
19#include <pch.h>
20#include <sound.h>
21#include <linux/bitops.h>
22#include <asm/global_data.h>
23
24static int bd82x6x_azalia_probe(struct udevice *dev)
25{
26	struct pci_child_plat *plat;
27	struct hda_codec_priv *priv;
28	struct udevice *pch;
29	u32 codec_mask;
30	int conf;
31	int ret;
32
33	/* Only init after relocation */
34	if (!(gd->flags & GD_FLG_RELOC))
35		return 0;
36
37	ret = hda_codec_init(dev);
38	if (ret) {
39		log_debug("Cannot set up HDA codec (err=%d)\n", ret);
40		return ret;
41	}
42	priv = dev_get_priv(dev);
43
44	ret = uclass_first_device_err(UCLASS_PCH, &pch);
45	log_debug("PCH %p %s\n", pch, pch->name);
46	if (ret)
47		return ret;
48
49	conf = pch_ioctl(pch, PCH_REQ_HDA_CONFIG, NULL, 0);
50	log_debug("conf = %x\n", conf);
51	if (conf >= 0) {
52		dm_pci_clrset_config32(dev, 0x120, 7 << 24 | 0xfe,
53				       1 << 24 | /* 2 << 24 for server */
54				       conf);
55
56		dm_pci_clrset_config16(dev, 0x78, 0, 1 << 1);
57	} else {
58		log_debug("V1CTL disabled\n");
59	}
60	dm_pci_clrset_config32(dev, 0x114, 0xfe, 0);
61
62	/* Set VCi enable bit */
63	dm_pci_clrset_config32(dev, 0x120, 0, 1U << 31);
64
65	/* Enable HDMI codec */
66	dm_pci_clrset_config32(dev, 0xc4, 0, 1 << 1);
67	dm_pci_clrset_config8(dev, 0x43, 0, 1 << 6);
68
69	/* Additional programming steps */
70	dm_pci_clrset_config32(dev, 0xc4, 0, 1 << 13);
71	dm_pci_clrset_config32(dev, 0xc4, 0, 1 << 10);
72	dm_pci_clrset_config32(dev, 0xd0, 1U << 31, 0);
73
74	/* Additional step on Panther Point */
75	plat = dev_get_parent_plat(dev);
76	if (plat->device == PCI_DEVICE_ID_INTEL_PANTHERPOINT_HDA)
77		dm_pci_clrset_config32(dev, 0xc4, 0, 1 << 17);
78
79	dm_pci_write_config8(dev, 0x3c, 0xa); /* unused? */
80
81	/* Audio Control: Select Azalia mode */
82	dm_pci_clrset_config8(dev, 0x40, 0, 1);
83	dm_pci_clrset_config8(dev, 0x4d, 1 << 7, 0); /* Docking not supported */
84	codec_mask = hda_codec_detect(priv->regs);
85	log_debug("codec_mask = %02x\n", codec_mask);
86
87	if (codec_mask) {
88		ret = hda_codecs_init(dev, priv->regs, codec_mask);
89		if (ret) {
90			log_err("Codec init failed (err=%d)\n", ret);
91			return ret;
92		}
93	}
94
95	/* Enable dynamic clock gating */
96	dm_pci_clrset_config8(dev, 0x43, 7, BIT(2) | BIT(0));
97
98	ret = hda_codec_finish_init(dev);
99	if (ret) {
100		log_debug("Cannot set up HDA codec (err=%d)\n", ret);
101		return ret;
102	}
103
104	return 0;
105}
106
107static int bd82x6x_azalia_setup(struct udevice *dev)
108{
109	return 0;
110}
111
112int bd82x6x_azalia_start_beep(struct udevice *dev, int frequency_hz)
113{
114	return hda_codec_start_beep(dev, frequency_hz);
115}
116
117int bd82x6x_azalia_stop_beep(struct udevice *dev)
118{
119	return hda_codec_stop_beep(dev);
120}
121
122static const struct sound_ops bd82x6x_azalia_ops = {
123	.setup		= bd82x6x_azalia_setup,
124	.start_beep	= bd82x6x_azalia_start_beep,
125	.stop_beep	= bd82x6x_azalia_stop_beep,
126};
127
128static const struct udevice_id bd82x6x_azalia_ids[] = {
129	{ .compatible = "intel,hd-audio" },
130	{ }
131};
132
133U_BOOT_DRIVER(bd82x6x_azalia_drv) = {
134	.name		= "bd82x6x-hda",
135	.id		= UCLASS_SOUND,
136	.of_match	= bd82x6x_azalia_ids,
137	.probe		= bd82x6x_azalia_probe,
138	.ops		= &bd82x6x_azalia_ops,
139	.priv_auto	= sizeof(struct hda_codec_priv),
140};
141