summaryrefslogtreecommitdiff
path: root/board/chromebook-x86/chromeos/hda_codec.c
blob: 21a0db5a56648d6384711fd1d94454fa82a5feae (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
140
141
142
143
144
145
/*
 * Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 *
 * Alternatively, this software may be distributed under the terms of the
 * GNU General Public License ("GPL") version 2 as published by the Free
 * Software Foundation.
 */

/* Implementation of per-board codec beeping */

#include <chromeos/hda_codec.h>
#include <common.h>
#include <asm/global_data.h>
#include <asm/io.h>
#include <pci.h>

#define HDA_CMD_REG 0x5C
#define HDA_ICII_REG 0x64
#define   HDA_ICII_BUSY (1 << 0)
#define   HDA_ICII_VALID  (1 << 1)

/**
 *  Wait 50usec for the codec to indicate it is ready
 *  no response would imply that the codec is non-operative
 */
static int wait_for_ready(uint32_t base)
{
	/* Use a 50 usec timeout - the Linux kernel uses the
	 * same duration
	 */

	int timeout = 50;

	while (timeout--) {
		uint32_t reg32 = readl(base +  HDA_ICII_REG);
		asm("" ::: "memory");
		if (!(reg32 & HDA_ICII_BUSY))
			return 0;
		udelay(1);
	}

	return -1;
}

/**
 *  Wait 50usec for the codec to indicate that it accepted
 *  the previous command.  No response would imply that the code
 *  is non-operative
 */
static int wait_for_valid(uint32_t base)
{
	uint32_t reg32;

	/* Send the verb to the codec */
	reg32 = readl(base + HDA_ICII_REG);
	reg32 |= HDA_ICII_BUSY | HDA_ICII_VALID;
	writel(reg32, base + HDA_ICII_REG);

	/* Use a 50 usec timeout - the Linux kernel uses the
	 * same duration
	 */

	int timeout = 50;
	while (timeout--) {
		reg32 = readl(base + HDA_ICII_REG);
		if ((reg32 & (HDA_ICII_VALID | HDA_ICII_BUSY)) ==
				HDA_ICII_VALID)
			return 0;
		udelay(1);
	}

	return -1;
}

/* Wait for the codec to be ready, write the verb, then wait for the
 * codec to be valid.
 */
int write_one_verb(uint32_t base, uint32_t val)
{
	if (wait_for_ready(base) == -1)
		return -1;

	writel(val, base + HDA_CMD_REG);

	if (wait_for_valid(base) == -1)
		return -1;

	return 0;
}

/* Supported sound devices.
 */
static struct pci_device_id supported[] = {
	{PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_COUGARPOINT_HDA},
	{}
};

/* Find the base address to talk tot he HDA codec.
 */
static u32 get_hda_base(void)
{
	pci_dev_t devbusfn;
	u32 pci_mem_base;

	devbusfn = pci_find_devices(supported, 0);
	if (devbusfn < 0) {
		printf("Audio: Controller not found !\n");
		return 0;
	}

	pci_read_config_dword(devbusfn, PCI_BASE_ADDRESS_0, &pci_mem_base);
	pci_mem_base = pci_mem_to_phys(devbusfn, pci_mem_base);
	return pci_mem_base;
}

static const u32 beep_cmd[] = {
	0x00170500, /* power up codec */
	0x00270500, /* power up DAC */
	0x00670500, /* power up speaker */
	0x00670740, /* enable speaker output */
	0x0023B04B, /* set DAC gain */
	0x00C70A0C, /* enable beep generator 1 kHz */
};

void enable_beep(void)
{
	uint32_t base;
	int i;

	base = get_hda_base();
	for (i = 0; i < sizeof(beep_cmd)/sizeof(beep_cmd[0]); i++) {
		if (write_one_verb(base, beep_cmd[i]))
			return;
	}
}

void disable_beep(void)
{
	uint32_t base;

	base = get_hda_base();
	write_one_verb(base, 0x00C70A00); /* Disable beep gen */
}