summaryrefslogtreecommitdiffstats
path: root/sound/pci/ice1712/juli.c
blob: 4550609b4d478659c15c7213dafcb9d2d7886e7c (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
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
/*
 *   ALSA driver for ICEnsemble VT1724 (Envy24HT)
 *
 *   Lowlevel functions for ESI Juli@ cards
 *
 *	Copyright (c) 2004 Jaroslav Kysela <perex@perex.cz>
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 *
 */      

#include <asm/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <sound/core.h>

#include "ice1712.h"
#include "envy24ht.h"
#include "juli.h"

struct juli_spec {
	struct ak4114 *ak4114;
	unsigned int analog: 1;
};

/*
 * chip addresses on I2C bus
 */
#define AK4114_ADDR		0x20		/* S/PDIF receiver */
#define AK4358_ADDR		0x22		/* DAC */

/*
 * GPIO pins
 */
#define GPIO_FREQ_MASK		(3<<0)
#define GPIO_FREQ_32KHZ		(0<<0)
#define GPIO_FREQ_44KHZ		(1<<0)
#define GPIO_FREQ_48KHZ		(2<<0)
#define GPIO_MULTI_MASK		(3<<2)
#define GPIO_MULTI_4X		(0<<2)
#define GPIO_MULTI_2X		(1<<2)
#define GPIO_MULTI_1X		(2<<2)		/* also external */
#define GPIO_MULTI_HALF		(3<<2)
#define GPIO_INTERNAL_CLOCK	(1<<4)
#define GPIO_ANALOG_PRESENT	(1<<5)		/* RO only: 0 = present */
#define GPIO_RXMCLK_SEL		(1<<7)		/* must be 0 */
#define GPIO_AK5385A_CKS0	(1<<8)
#define GPIO_AK5385A_DFS0	(1<<9)		/* swapped with DFS1 according doc? */
#define GPIO_AK5385A_DFS1	(1<<10)
#define GPIO_DIGOUT_MONITOR	(1<<11)		/* 1 = active */
#define GPIO_DIGIN_MONITOR	(1<<12)		/* 1 = active */
#define GPIO_ANAIN_MONITOR	(1<<13)		/* 1 = active */
#define GPIO_AK5385A_MCLK	(1<<14)		/* must be 0 */
#define GPIO_MUTE_CONTROL	(1<<15)		/* 0 = off, 1 = on */

static void juli_ak4114_write(void *private_data, unsigned char reg, unsigned char val)
{
	snd_vt1724_write_i2c((struct snd_ice1712 *)private_data, AK4114_ADDR, reg, val);
}
        
static unsigned char juli_ak4114_read(void *private_data, unsigned char reg)
{
	return snd_vt1724_read_i2c((struct snd_ice1712 *)private_data, AK4114_ADDR, reg);
}

static void juli_spdif_in_open(struct snd_ice1712 *ice,
			       struct snd_pcm_substream *substream)
{
	struct juli_spec *spec = ice->spec;
	struct snd_pcm_runtime *runtime = substream->runtime;
	int rate;

	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
		return;
	rate = snd_ak4114_external_rate(spec->ak4114);
	if (rate >= runtime->hw.rate_min && rate <= runtime->hw.rate_max) {
		runtime->hw.rate_min = rate;
		runtime->hw.rate_max = rate;
	}
}

/*
 * AK4358 section
 */

static void juli_akm_lock(struct snd_akm4xxx *ak, int chip)
{
}

static void juli_akm_unlock(struct snd_akm4xxx *ak, int chip)
{
}

static void juli_akm_write(struct snd_akm4xxx *ak, int chip,
			   unsigned char addr, unsigned char data)
{
	struct snd_ice1712 *ice = ak->private_data[0];
	 
	snd_assert(chip == 0, return);
	snd_vt1724_write_i2c(ice, AK4358_ADDR, addr, data);
}

/*
 * change the rate of envy24HT, AK4358
 */
static void juli_akm_set_rate_val(struct snd_akm4xxx *ak, unsigned int rate)
{
	unsigned char old, tmp, dfs;

	if (rate == 0)  /* no hint - S/PDIF input is master, simply return */
		return;
	
	/* adjust DFS on codecs */
	if (rate > 96000) 
		dfs = 2;
	else if (rate > 48000)
		dfs = 1;
	else
		dfs = 0;
	
	tmp = snd_akm4xxx_get(ak, 0, 2);
	old = (tmp >> 4) & 0x03;
	if (old == dfs)
		return;
	/* reset DFS */
	snd_akm4xxx_reset(ak, 1);
	tmp = snd_akm4xxx_get(ak, 0, 2);
	tmp &= ~(0x03 << 4);
	tmp |= dfs << 4;
	snd_akm4xxx_set(ak, 0, 2, tmp);
	snd_akm4xxx_reset(ak, 0);
}

static struct snd_akm4xxx akm_juli_dac __devinitdata = {
	.type = SND_AK4358,
	.num_dacs = 2,
	.ops = {
		.lock = juli_akm_lock,
		.unlock = juli_akm_unlock,
		.write = juli_akm_write,
		.set_rate_val = juli_akm_set_rate_val
	}
};

static int __devinit juli_add_controls(struct snd_ice1712 *ice)
{
	struct juli_spec *spec = ice->spec;
	int err;
	err = snd_ice1712_akm4xxx_build_controls(ice);
	if (err < 0)
		return err;
	/* only capture SPDIF over AK4114 */
	err = snd_ak4114_build(spec->ak4114, NULL,
			       ice->pcm_pro->streams[SNDRV_PCM_STREAM_CAPTURE].substream);
	if (err < 0)
		return err;
	return 0;
}

/*
 * initialize the chip
 */
static int __devinit juli_init(struct snd_ice1712 *ice)
{
	static const unsigned char ak4114_init_vals[] = {
		/* AK4117_REG_PWRDN */	AK4114_RST | AK4114_PWN | AK4114_OCKS0 | AK4114_OCKS1,
		/* AK4114_REQ_FORMAT */	AK4114_DIF_I24I2S,
		/* AK4114_REG_IO0 */	AK4114_TX1E,
		/* AK4114_REG_IO1 */	AK4114_EFH_1024 | AK4114_DIT | AK4114_IPS(1),
		/* AK4114_REG_INT0_MASK */ 0,
		/* AK4114_REG_INT1_MASK */ 0
	};
	static const unsigned char ak4114_init_txcsb[] = {
		0x41, 0x02, 0x2c, 0x00, 0x00
	};
	int err;
	struct juli_spec *spec;
	struct snd_akm4xxx *ak;

	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
	if (!spec)
		return -ENOMEM;
	ice->spec = spec;

	err = snd_ak4114_create(ice->card,
				juli_ak4114_read,
				juli_ak4114_write,
				ak4114_init_vals, ak4114_init_txcsb,
				ice, &spec->ak4114);
	if (err < 0)
		return err;

#if 0
        /* it seems that the analog doughter board detection does not work
           reliably, so force the analog flag; it should be very rare
           to use Juli@ without the analog doughter board */
	spec->analog = (ice->gpio.get_data(ice) & GPIO_ANALOG_PRESENT) ? 0 : 1;
#else
        spec->analog = 1;
#endif

	if (spec->analog) {
		printk(KERN_INFO "juli@: analog I/O detected\n");
		ice->num_total_dacs = 2;
		ice->num_total_adcs = 2;

		ak = ice->akm = kzalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL);
		if (! ak)
			return -ENOMEM;
		ice->akm_codecs = 1;
		if ((err = snd_ice1712_akm4xxx_init(ak, &akm_juli_dac, NULL, ice)) < 0)
			return err;
	}
	
	ice->spdif.ops.open = juli_spdif_in_open;
	return 0;
}


/*
 * Juli@ boards don't provide the EEPROM data except for the vendor IDs.
 * hence the driver needs to sets up it properly.
 */

static unsigned char juli_eeprom[] __devinitdata = {
	[ICE_EEP2_SYSCONF]     = 0x20,	/* clock 512, mpu401, 1xADC, 1xDACs */
	[ICE_EEP2_ACLINK]      = 0x80,	/* I2S */
	[ICE_EEP2_I2S]         = 0xf8,	/* vol, 96k, 24bit, 192k */
	[ICE_EEP2_SPDIF]       = 0xc3,	/* out-en, out-int, spdif-in */
	[ICE_EEP2_GPIO_DIR]    = 0x9f,
	[ICE_EEP2_GPIO_DIR1]   = 0xff,
	[ICE_EEP2_GPIO_DIR2]   = 0x7f,
	[ICE_EEP2_GPIO_MASK]   = 0x9f,
	[ICE_EEP2_GPIO_MASK1]  = 0xff,
	[ICE_EEP2_GPIO_MASK2]  = 0x7f,
	[ICE_EEP2_GPIO_STATE]  = 0x16,	/* internal clock, multiple 1x, 48kHz */
	[ICE_EEP2_GPIO_STATE1] = 0x80,	/* mute */
	[ICE_EEP2_GPIO_STATE2] = 0x00,
};

/* entry point */
struct snd_ice1712_card_info snd_vt1724_juli_cards[] __devinitdata = {
	{
		.subvendor = VT1724_SUBDEVICE_JULI,
		.name = "ESI Juli@",
		.model = "juli",
		.chip_init = juli_init,
		.build_controls = juli_add_controls,
		.eeprom_size = sizeof(juli_eeprom),
		.eeprom_data = juli_eeprom,
	},
	{ } /* terminator */
};
OpenPOWER on IntegriCloud