summaryrefslogtreecommitdiff
path: root/board/chromebook-x86/chromeos/hda_codec.c
blob: e6f597fd8420204cd5da68713dc6f4dfab7f2f76 (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
146
147
148
149
150
151
152
153
154
155
156
157
158
/*
 * 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 <common.h>
#include <asm/global_data.h>
#include <asm/io.h>
#include <pci.h>
#include <chromeos/hda_codec.h>

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

#define BEEP_FREQ_MAGIC 0x00C70A00
#define BEEP_FREQ_BASE 12000

/**
 *  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 */
};					/* and follow with BEEP_FREQ_MAGIC */

void enable_beep(uint32_t frequency)
{
	uint32_t base;
	uint8_t divider_val;
	int i;

	if (0 == frequency)
		divider_val = 0;	/* off */
	else if (frequency > BEEP_FREQ_BASE)
		divider_val = 1;
	else if (frequency < BEEP_FREQ_BASE / 0xFF)
		divider_val = 0xff;
	else
		divider_val = (uint8_t)(0xFF & (BEEP_FREQ_BASE / frequency));

	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;
	}
	write_one_verb(base, BEEP_FREQ_MAGIC|divider_val);
}

void disable_beep(void)
{
	uint32_t base;

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