summaryrefslogtreecommitdiff
path: root/drivers/sound/ivybridge_sound.c
blob: bc3c1e85f8cfb7020132d8d63c6d5e6a9b92080f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
// SPDX-License-Identifier: GPL-2.0
/*
 * Intel HDA audio (Azalia) for ivybridge
 *
 * Originally from coreboot file bd82x6x/azalia.c
 *
 * Copyright (C) 2008 Advanced Micro Devices, Inc.
 * Copyright (C) 2008-2009 coresystems GmbH
 * Copyright (C) 2011 The ChromiumOS Authors.
 * Copyright 2018 Google LLC
 */

#define LOG_CATEGORY UCLASS_SOUND

#include <common.h>
#include <dm.h>
#include <hda_codec.h>
#include <log.h>
#include <pch.h>
#include <sound.h>
#include <linux/bitops.h>

static int bd82x6x_azalia_probe(struct udevice *dev)
{
	struct pci_child_platdata *plat;
	struct hda_codec_priv *priv;
	struct udevice *pch;
	u32 codec_mask;
	int conf;
	int ret;

	/* Only init after relocation */
	if (!(gd->flags & GD_FLG_RELOC))
		return 0;

	ret = hda_codec_init(dev);
	if (ret) {
		log_debug("Cannot set up HDA codec (err=%d)\n", ret);
		return ret;
	}
	priv = dev_get_priv(dev);

	ret = uclass_first_device_err(UCLASS_PCH, &pch);
	log_debug("PCH %p %s\n", pch, pch->name);
	if (ret)
		return ret;

	conf = pch_ioctl(pch, PCH_REQ_HDA_CONFIG, NULL, 0);
	log_debug("conf = %x\n", conf);
	if (conf >= 0) {
		dm_pci_clrset_config32(dev, 0x120, 7 << 24 | 0xfe,
				       1 << 24 | /* 2 << 24 for server */
				       conf);

		dm_pci_clrset_config16(dev, 0x78, 0, 1 << 1);
	} else {
		log_debug("V1CTL disabled\n");
	}
	dm_pci_clrset_config32(dev, 0x114, 0xfe, 0);

	/* Set VCi enable bit */
	dm_pci_clrset_config32(dev, 0x120, 0, 1U << 31);

	/* Enable HDMI codec */
	dm_pci_clrset_config32(dev, 0xc4, 0, 1 << 1);
	dm_pci_clrset_config8(dev, 0x43, 0, 1 << 6);

	/* Additional programming steps */
	dm_pci_clrset_config32(dev, 0xc4, 0, 1 << 13);
	dm_pci_clrset_config32(dev, 0xc4, 0, 1 << 10);
	dm_pci_clrset_config32(dev, 0xd0, 1U << 31, 0);

	/* Additional step on Panther Point */
	plat = dev_get_parent_platdata(dev);
	if (plat->device == PCI_DEVICE_ID_INTEL_PANTHERPOINT_HDA)
		dm_pci_clrset_config32(dev, 0xc4, 0, 1 << 17);

	dm_pci_write_config8(dev, 0x3c, 0xa); /* unused? */

	/* Audio Control: Select Azalia mode */
	dm_pci_clrset_config8(dev, 0x40, 0, 1);
	dm_pci_clrset_config8(dev, 0x4d, 1 << 7, 0); /* Docking not supported */
	codec_mask = hda_codec_detect(priv->regs);
	log_debug("codec_mask = %02x\n", codec_mask);

	if (codec_mask) {
		ret = hda_codecs_init(dev, priv->regs, codec_mask);
		if (ret) {
			log_err("Codec init failed (err=%d)\n", ret);
			return ret;
		}
	}

	/* Enable dynamic clock gating */
	dm_pci_clrset_config8(dev, 0x43, 7, BIT(2) | BIT(0));

	ret = hda_codec_finish_init(dev);
	if (ret) {
		log_debug("Cannot set up HDA codec (err=%d)\n", ret);
		return ret;
	}

	return 0;
}

static int bd82x6x_azalia_setup(struct udevice *dev)
{
	return 0;
}

int bd82x6x_azalia_start_beep(struct udevice *dev, int frequency_hz)
{
	return hda_codec_start_beep(dev, frequency_hz);
}

int bd82x6x_azalia_stop_beep(struct udevice *dev)
{
	return hda_codec_stop_beep(dev);
}

static const struct sound_ops bd82x6x_azalia_ops = {
	.setup		= bd82x6x_azalia_setup,
	.start_beep	= bd82x6x_azalia_start_beep,
	.stop_beep	= bd82x6x_azalia_stop_beep,
};

static const struct udevice_id bd82x6x_azalia_ids[] = {
	{ .compatible = "intel,hd-audio" },
	{ }
};

U_BOOT_DRIVER(bd82x6x_azalia_drv) = {
	.name		= "bd82x6x-hda",
	.id		= UCLASS_SOUND,
	.of_match	= bd82x6x_azalia_ids,
	.probe		= bd82x6x_azalia_probe,
	.ops		= &bd82x6x_azalia_ops,
	.priv_auto_alloc_size	= sizeof(struct hda_codec_priv),
};