au8522.c revision 1.2.6.3 1 /* $NetBSD: au8522.c,v 1.2.6.3 2011/05/31 03:04:36 rmind Exp $ */
2
3 /*-
4 * Copyright (c) 2010 Jared D. McNeill <jmcneill (at) invisible.ca>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 /*
30 * Auvitek AU8522
31 */
32
33 #include <sys/cdefs.h>
34 __KERNEL_RCSID(0, "$NetBSD: au8522.c,v 1.2.6.3 2011/05/31 03:04:36 rmind Exp $");
35
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/device.h>
39 #include <sys/conf.h>
40 #include <sys/bus.h>
41 #include <sys/kmem.h>
42 #include <sys/module.h>
43
44 #include <dev/i2c/i2cvar.h>
45
46 #include <dev/i2c/au8522reg.h>
47 #include <dev/i2c/au8522var.h>
48
49 static int au8522_reset(struct au8522 *);
50 static int au8522_read_1(struct au8522 *, uint16_t, uint8_t *);
51 static int au8522_write_1(struct au8522 *, uint16_t, uint8_t);
52 static int au8522_set_vinput(struct au8522 *, au8522_vinput_t);
53 static int au8522_set_ainput(struct au8522 *, au8522_ainput_t);
54 static void au8522_set_common(struct au8522 *, au8522_vinput_t);
55
56 static int
57 au8522_reset(struct au8522 *au)
58 {
59 return au8522_write_1(au, 0xa4, 1 << 5);
60 }
61
62 static int
63 au8522_read_1(struct au8522 *au, uint16_t reg, uint8_t *val)
64 {
65 uint8_t cmd[2];
66 int error;
67
68 cmd[0] = (reg >> 8) | 0x40;
69 cmd[1] = reg & 0xff;
70 error = iic_exec(au->i2c, I2C_OP_WRITE, au->i2c_addr,
71 cmd, sizeof(cmd), NULL, 0, 0);
72 if (error)
73 return error;
74 return iic_exec(au->i2c, I2C_OP_READ, au->i2c_addr,
75 NULL, 0, val, sizeof(*val), 0);
76 }
77
78 static int
79 au8522_write_1(struct au8522 *au, uint16_t reg, uint8_t val)
80 {
81 uint8_t data[3];
82
83 data[0] = (reg >> 8) | 0x80;
84 data[1] = reg & 0xff;
85 data[2] = val;
86 return iic_exec(au->i2c, I2C_OP_WRITE, au->i2c_addr,
87 data, sizeof(data), NULL, 0, 0);
88 }
89
90 static int
91 au8522_set_vinput(struct au8522 *au, au8522_vinput_t vi)
92 {
93 switch (vi) {
94 case AU8522_VINPUT_CVBS:
95 au8522_write_1(au, AU8522_REG_MODCLKCTL, AU8522_MODCLKCTL_CVBS);
96 au8522_write_1(au, AU8522_REG_PGACTL, 0x00);
97 au8522_write_1(au, AU8522_REG_CLAMPCTL, 0x0e);
98 au8522_write_1(au, AU8522_REG_PGACTL, 0x10);
99 au8522_write_1(au, AU8522_REG_INPUTCTL,
100 AU8522_INPUTCTL_CVBS_CH1);
101
102 au8522_set_common(au, vi);
103
104 au8522_write_1(au, AU8522_REG_SYSMODCTL0,
105 AU8522_SYSMODCTL0_CVBS);
106 break;
107 case AU8522_VINPUT_SVIDEO:
108 au8522_write_1(au, AU8522_REG_MODCLKCTL,
109 AU8522_MODCLKCTL_SVIDEO);
110 au8522_write_1(au, AU8522_REG_INPUTCTL,
111 AU8522_INPUTCTL_SVIDEO_CH13);
112 au8522_write_1(au, AU8522_REG_CLAMPCTL, 0x00);
113
114 au8522_set_common(au, vi);
115
116 au8522_write_1(au, AU8522_REG_SYSMODCTL0,
117 AU8522_SYSMODCTL0_CVBS);
118
119 break;
120 case AU8522_VINPUT_CVBS_TUNER:
121 au8522_write_1(au, AU8522_REG_MODCLKCTL, AU8522_MODCLKCTL_CVBS);
122 au8522_write_1(au, AU8522_REG_PGACTL, 0x00);
123 au8522_write_1(au, AU8522_REG_CLAMPCTL, 0x0e);
124 au8522_write_1(au, AU8522_REG_PGACTL, 0x10);
125 au8522_write_1(au, AU8522_REG_INPUTCTL,
126 AU8522_INPUTCTL_CVBS_CH4_SIF);
127
128 au8522_set_common(au, vi);
129
130 au8522_write_1(au, AU8522_REG_SYSMODCTL0,
131 AU8522_SYSMODCTL0_CVBS);
132
133 break;
134 default:
135 return EINVAL;
136 }
137
138 return 0;
139 }
140
141 static void
142 au8522_set_common(struct au8522 *au, au8522_vinput_t vi)
143 {
144 au8522_write_1(au, AU8522_REG_INTMASK, 0x00);
145 au8522_write_1(au, AU8522_REG_VIDEOMODE, vi == AU8522_VINPUT_SVIDEO ?
146 AU8522_VIDEOMODE_SVIDEO : AU8522_VIDEOMODE_CVBS);
147 au8522_write_1(au, AU8522_REG_TV_PGA, AU8522_TV_PGA_CVBS);
148 }
149
150 static int
151 au8522_set_ainput(struct au8522 *au, au8522_ainput_t ai)
152 {
153 /* mute during mode change */
154 au8522_write_1(au, AU8522_REG_AUDIO_VOL_L, 0x00);
155 au8522_write_1(au, AU8522_REG_AUDIO_VOL_R, 0x00);
156 au8522_write_1(au, AU8522_REG_AUDIO_VOL, 0x00);
157
158 switch (ai) {
159 case AU8522_AINPUT_SIF:
160 au8522_write_1(au, AU8522_REG_SYSMODCTL0,
161 AU8522_SYSMODCTL0_CVBS);
162 au8522_write_1(au, AU8522_REG_AUDIO_MODE, 0x82);
163 au8522_write_1(au, AU8522_REG_SYSMODCTL1,
164 AU8522_SYSMODCTL1_I2S);
165 au8522_write_1(au, AU8522_REG_AUDIO_FREQ, 0x03);
166 au8522_write_1(au, AU8522_REG_I2S_CTL2, 0xc2);
167 /* unmute */
168 au8522_write_1(au, AU8522_REG_AUDIO_VOL_L, 0x7f);
169 au8522_write_1(au, AU8522_REG_AUDIO_VOL_R, 0x7f);
170 au8522_write_1(au, AU8522_REG_AUDIO_VOL, 0xff);
171 break;
172 case AU8522_AINPUT_NONE:
173 au8522_write_1(au, AU8522_REG_USBEN, 0x00);
174 au8522_write_1(au, AU8522_REG_AUDIO_VOL_L, 0x7f);
175 au8522_write_1(au, AU8522_REG_AUDIO_VOL_R, 0x7f);
176 au8522_write_1(au, AU8522_REG_AUDIO_MODE, 0x40);
177 au8522_write_1(au, AU8522_REG_SYSMODCTL1,
178 AU8522_SYSMODCTL1_SVIDEO);
179 au8522_write_1(au, AU8522_REG_AUDIO_FREQ, 0x03);
180 au8522_write_1(au, AU8522_REG_I2S_CTL2, 0x02);
181 au8522_write_1(au, AU8522_REG_SYSMODCTL0,
182 AU8522_SYSMODCTL0_CVBS);
183 break;
184 default:
185 return EINVAL;
186 }
187 return 0;
188 }
189
190 struct au8522 *
191 au8522_open(device_t parent, i2c_tag_t i2c, i2c_addr_t addr)
192 {
193 struct au8522 *au;
194
195 au = kmem_alloc(sizeof(*au), KM_SLEEP);
196 if (au == NULL)
197 return NULL;
198 au->parent = parent;
199 au->i2c = i2c;
200 au->i2c_addr = addr;
201
202 if (au8522_reset(au))
203 goto failed;
204 if (au8522_write_1(au, AU8522_REG_TUNERCTL, AU8522_TUNERCTL_EN))
205 goto failed;
206
207 return au;
208
209 failed:
210 kmem_free(au, sizeof(*au));
211 return NULL;
212 }
213
214 void
215 au8522_close(struct au8522 *au)
216 {
217 kmem_free(au, sizeof(*au));
218 }
219
220 void
221 au8522_enable(struct au8522 *au, bool enable)
222 {
223 if (enable) {
224 au8522_write_1(au, AU8522_REG_SYSMODCTL0,
225 AU8522_SYSMODCTL0_RESET);
226 delay(1000);
227 au8522_write_1(au, AU8522_REG_SYSMODCTL0,
228 AU8522_SYSMODCTL0_CVBS);
229 } else {
230 au8522_write_1(au, AU8522_REG_SYSMODCTL0,
231 AU8522_SYSMODCTL0_DISABLE);
232 }
233 }
234
235 void
236 au8522_set_input(struct au8522 *au, au8522_vinput_t vi, au8522_ainput_t ai)
237 {
238 au8522_reset(au);
239
240 if (vi != AU8522_VINPUT_UNCONF)
241 au8522_set_vinput(au, vi);
242 if (ai != AU8522_AINPUT_UNCONF)
243 au8522_set_ainput(au, ai);
244 }
245
246 int
247 au8522_get_signal(struct au8522 *au)
248 {
249 uint8_t status;
250
251 if (au8522_read_1(au, AU8522_REG_STATUS, &status))
252 return 0;
253
254 #ifdef AU8522_DEBUG
255 printf("au8522: status=0x%02x\n", status);
256 #endif
257 return (status & AU8522_STATUS_LOCK) == AU8522_STATUS_LOCK ? 1 : 0;
258 }
259
260 void
261 au8522_set_audio(struct au8522 *au, bool onoff)
262 {
263 if (onoff) {
264 au8522_write_1(au, AU8522_REG_AUDIO_VOL_L, 0x7f);
265 au8522_write_1(au, AU8522_REG_AUDIO_VOL_R, 0x7f);
266 au8522_write_1(au, AU8522_REG_AUDIO_VOL, 0xff);
267 } else {
268 au8522_write_1(au, AU8522_REG_AUDIO_VOL_L, 0x00);
269 au8522_write_1(au, AU8522_REG_AUDIO_VOL_R, 0x00);
270 au8522_write_1(au, AU8522_REG_AUDIO_VOL, 0x00);
271 }
272 }
273
274 MODULE(MODULE_CLASS_DRIVER, au8522, NULL);
275
276 static int
277 au8522_modcmd(modcmd_t cmd, void *opaque)
278 {
279 switch (cmd) {
280 case MODULE_CMD_INIT:
281 return 0;
282 case MODULE_CMD_FINI:
283 return 0;
284 default:
285 return ENOTTY;
286 }
287 }
288