au8522.c revision 1.4 1 1.4 jmcneill /* $NetBSD: au8522.c,v 1.4 2011/07/09 15:00:43 jmcneill Exp $ */
2 1.1 jmcneill
3 1.1 jmcneill /*-
4 1.1 jmcneill * Copyright (c) 2010 Jared D. McNeill <jmcneill (at) invisible.ca>
5 1.1 jmcneill * All rights reserved.
6 1.1 jmcneill *
7 1.1 jmcneill * Redistribution and use in source and binary forms, with or without
8 1.1 jmcneill * modification, are permitted provided that the following conditions
9 1.1 jmcneill * are met:
10 1.1 jmcneill * 1. Redistributions of source code must retain the above copyright
11 1.1 jmcneill * notice, this list of conditions and the following disclaimer.
12 1.1 jmcneill * 2. Redistributions in binary form must reproduce the above copyright
13 1.1 jmcneill * notice, this list of conditions and the following disclaimer in the
14 1.1 jmcneill * documentation and/or other materials provided with the distribution.
15 1.1 jmcneill *
16 1.1 jmcneill * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17 1.1 jmcneill * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 1.1 jmcneill * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 1.1 jmcneill * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20 1.1 jmcneill * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 1.1 jmcneill * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 1.1 jmcneill * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 1.1 jmcneill * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 1.1 jmcneill * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 1.1 jmcneill * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 1.1 jmcneill * POSSIBILITY OF SUCH DAMAGE.
27 1.1 jmcneill */
28 1.1 jmcneill
29 1.1 jmcneill /*
30 1.1 jmcneill * Auvitek AU8522
31 1.1 jmcneill */
32 1.1 jmcneill
33 1.1 jmcneill #include <sys/cdefs.h>
34 1.4 jmcneill __KERNEL_RCSID(0, "$NetBSD: au8522.c,v 1.4 2011/07/09 15:00:43 jmcneill Exp $");
35 1.1 jmcneill
36 1.1 jmcneill #include <sys/param.h>
37 1.1 jmcneill #include <sys/systm.h>
38 1.1 jmcneill #include <sys/device.h>
39 1.1 jmcneill #include <sys/conf.h>
40 1.1 jmcneill #include <sys/bus.h>
41 1.1 jmcneill #include <sys/kmem.h>
42 1.1 jmcneill #include <sys/module.h>
43 1.1 jmcneill
44 1.4 jmcneill #include <dev/dtv/dtvio.h>
45 1.4 jmcneill
46 1.1 jmcneill #include <dev/i2c/i2cvar.h>
47 1.1 jmcneill
48 1.1 jmcneill #include <dev/i2c/au8522reg.h>
49 1.1 jmcneill #include <dev/i2c/au8522var.h>
50 1.4 jmcneill #include <dev/i2c/au8522mod.h>
51 1.1 jmcneill
52 1.1 jmcneill static int au8522_reset(struct au8522 *);
53 1.1 jmcneill static int au8522_read_1(struct au8522 *, uint16_t, uint8_t *);
54 1.1 jmcneill static int au8522_write_1(struct au8522 *, uint16_t, uint8_t);
55 1.1 jmcneill static int au8522_set_vinput(struct au8522 *, au8522_vinput_t);
56 1.1 jmcneill static int au8522_set_ainput(struct au8522 *, au8522_ainput_t);
57 1.1 jmcneill static void au8522_set_common(struct au8522 *, au8522_vinput_t);
58 1.1 jmcneill
59 1.1 jmcneill static int
60 1.1 jmcneill au8522_reset(struct au8522 *au)
61 1.1 jmcneill {
62 1.1 jmcneill return au8522_write_1(au, 0xa4, 1 << 5);
63 1.1 jmcneill }
64 1.1 jmcneill
65 1.1 jmcneill static int
66 1.1 jmcneill au8522_read_1(struct au8522 *au, uint16_t reg, uint8_t *val)
67 1.1 jmcneill {
68 1.1 jmcneill uint8_t cmd[2];
69 1.1 jmcneill int error;
70 1.1 jmcneill
71 1.1 jmcneill cmd[0] = (reg >> 8) | 0x40;
72 1.1 jmcneill cmd[1] = reg & 0xff;
73 1.1 jmcneill error = iic_exec(au->i2c, I2C_OP_WRITE, au->i2c_addr,
74 1.1 jmcneill cmd, sizeof(cmd), NULL, 0, 0);
75 1.1 jmcneill if (error)
76 1.1 jmcneill return error;
77 1.1 jmcneill return iic_exec(au->i2c, I2C_OP_READ, au->i2c_addr,
78 1.1 jmcneill NULL, 0, val, sizeof(*val), 0);
79 1.1 jmcneill }
80 1.1 jmcneill
81 1.1 jmcneill static int
82 1.1 jmcneill au8522_write_1(struct au8522 *au, uint16_t reg, uint8_t val)
83 1.1 jmcneill {
84 1.1 jmcneill uint8_t data[3];
85 1.1 jmcneill
86 1.1 jmcneill data[0] = (reg >> 8) | 0x80;
87 1.1 jmcneill data[1] = reg & 0xff;
88 1.1 jmcneill data[2] = val;
89 1.1 jmcneill return iic_exec(au->i2c, I2C_OP_WRITE, au->i2c_addr,
90 1.1 jmcneill data, sizeof(data), NULL, 0, 0);
91 1.1 jmcneill }
92 1.1 jmcneill
93 1.1 jmcneill static int
94 1.1 jmcneill au8522_set_vinput(struct au8522 *au, au8522_vinput_t vi)
95 1.1 jmcneill {
96 1.1 jmcneill switch (vi) {
97 1.1 jmcneill case AU8522_VINPUT_CVBS:
98 1.1 jmcneill au8522_write_1(au, AU8522_REG_MODCLKCTL, AU8522_MODCLKCTL_CVBS);
99 1.1 jmcneill au8522_write_1(au, AU8522_REG_PGACTL, 0x00);
100 1.1 jmcneill au8522_write_1(au, AU8522_REG_CLAMPCTL, 0x0e);
101 1.1 jmcneill au8522_write_1(au, AU8522_REG_PGACTL, 0x10);
102 1.1 jmcneill au8522_write_1(au, AU8522_REG_INPUTCTL,
103 1.1 jmcneill AU8522_INPUTCTL_CVBS_CH1);
104 1.1 jmcneill
105 1.1 jmcneill au8522_set_common(au, vi);
106 1.1 jmcneill
107 1.1 jmcneill au8522_write_1(au, AU8522_REG_SYSMODCTL0,
108 1.1 jmcneill AU8522_SYSMODCTL0_CVBS);
109 1.1 jmcneill break;
110 1.1 jmcneill case AU8522_VINPUT_SVIDEO:
111 1.1 jmcneill au8522_write_1(au, AU8522_REG_MODCLKCTL,
112 1.1 jmcneill AU8522_MODCLKCTL_SVIDEO);
113 1.1 jmcneill au8522_write_1(au, AU8522_REG_INPUTCTL,
114 1.1 jmcneill AU8522_INPUTCTL_SVIDEO_CH13);
115 1.1 jmcneill au8522_write_1(au, AU8522_REG_CLAMPCTL, 0x00);
116 1.1 jmcneill
117 1.1 jmcneill au8522_set_common(au, vi);
118 1.1 jmcneill
119 1.1 jmcneill au8522_write_1(au, AU8522_REG_SYSMODCTL0,
120 1.1 jmcneill AU8522_SYSMODCTL0_CVBS);
121 1.1 jmcneill
122 1.1 jmcneill break;
123 1.1 jmcneill case AU8522_VINPUT_CVBS_TUNER:
124 1.1 jmcneill au8522_write_1(au, AU8522_REG_MODCLKCTL, AU8522_MODCLKCTL_CVBS);
125 1.1 jmcneill au8522_write_1(au, AU8522_REG_PGACTL, 0x00);
126 1.1 jmcneill au8522_write_1(au, AU8522_REG_CLAMPCTL, 0x0e);
127 1.1 jmcneill au8522_write_1(au, AU8522_REG_PGACTL, 0x10);
128 1.1 jmcneill au8522_write_1(au, AU8522_REG_INPUTCTL,
129 1.1 jmcneill AU8522_INPUTCTL_CVBS_CH4_SIF);
130 1.1 jmcneill
131 1.1 jmcneill au8522_set_common(au, vi);
132 1.1 jmcneill
133 1.1 jmcneill au8522_write_1(au, AU8522_REG_SYSMODCTL0,
134 1.1 jmcneill AU8522_SYSMODCTL0_CVBS);
135 1.1 jmcneill
136 1.1 jmcneill break;
137 1.1 jmcneill default:
138 1.1 jmcneill return EINVAL;
139 1.1 jmcneill }
140 1.1 jmcneill
141 1.1 jmcneill return 0;
142 1.1 jmcneill }
143 1.1 jmcneill
144 1.1 jmcneill static void
145 1.1 jmcneill au8522_set_common(struct au8522 *au, au8522_vinput_t vi)
146 1.1 jmcneill {
147 1.1 jmcneill au8522_write_1(au, AU8522_REG_INTMASK, 0x00);
148 1.1 jmcneill au8522_write_1(au, AU8522_REG_VIDEOMODE, vi == AU8522_VINPUT_SVIDEO ?
149 1.1 jmcneill AU8522_VIDEOMODE_SVIDEO : AU8522_VIDEOMODE_CVBS);
150 1.1 jmcneill au8522_write_1(au, AU8522_REG_TV_PGA, AU8522_TV_PGA_CVBS);
151 1.1 jmcneill }
152 1.1 jmcneill
153 1.1 jmcneill static int
154 1.1 jmcneill au8522_set_ainput(struct au8522 *au, au8522_ainput_t ai)
155 1.1 jmcneill {
156 1.1 jmcneill /* mute during mode change */
157 1.1 jmcneill au8522_write_1(au, AU8522_REG_AUDIO_VOL_L, 0x00);
158 1.1 jmcneill au8522_write_1(au, AU8522_REG_AUDIO_VOL_R, 0x00);
159 1.1 jmcneill au8522_write_1(au, AU8522_REG_AUDIO_VOL, 0x00);
160 1.1 jmcneill
161 1.1 jmcneill switch (ai) {
162 1.1 jmcneill case AU8522_AINPUT_SIF:
163 1.1 jmcneill au8522_write_1(au, AU8522_REG_SYSMODCTL0,
164 1.1 jmcneill AU8522_SYSMODCTL0_CVBS);
165 1.1 jmcneill au8522_write_1(au, AU8522_REG_AUDIO_MODE, 0x82);
166 1.1 jmcneill au8522_write_1(au, AU8522_REG_SYSMODCTL1,
167 1.1 jmcneill AU8522_SYSMODCTL1_I2S);
168 1.1 jmcneill au8522_write_1(au, AU8522_REG_AUDIO_FREQ, 0x03);
169 1.1 jmcneill au8522_write_1(au, AU8522_REG_I2S_CTL2, 0xc2);
170 1.1 jmcneill /* unmute */
171 1.1 jmcneill au8522_write_1(au, AU8522_REG_AUDIO_VOL_L, 0x7f);
172 1.1 jmcneill au8522_write_1(au, AU8522_REG_AUDIO_VOL_R, 0x7f);
173 1.1 jmcneill au8522_write_1(au, AU8522_REG_AUDIO_VOL, 0xff);
174 1.1 jmcneill break;
175 1.1 jmcneill case AU8522_AINPUT_NONE:
176 1.1 jmcneill au8522_write_1(au, AU8522_REG_USBEN, 0x00);
177 1.1 jmcneill au8522_write_1(au, AU8522_REG_AUDIO_VOL_L, 0x7f);
178 1.1 jmcneill au8522_write_1(au, AU8522_REG_AUDIO_VOL_R, 0x7f);
179 1.1 jmcneill au8522_write_1(au, AU8522_REG_AUDIO_MODE, 0x40);
180 1.1 jmcneill au8522_write_1(au, AU8522_REG_SYSMODCTL1,
181 1.1 jmcneill AU8522_SYSMODCTL1_SVIDEO);
182 1.1 jmcneill au8522_write_1(au, AU8522_REG_AUDIO_FREQ, 0x03);
183 1.1 jmcneill au8522_write_1(au, AU8522_REG_I2S_CTL2, 0x02);
184 1.1 jmcneill au8522_write_1(au, AU8522_REG_SYSMODCTL0,
185 1.1 jmcneill AU8522_SYSMODCTL0_CVBS);
186 1.1 jmcneill break;
187 1.1 jmcneill default:
188 1.1 jmcneill return EINVAL;
189 1.1 jmcneill }
190 1.1 jmcneill return 0;
191 1.1 jmcneill }
192 1.1 jmcneill
193 1.4 jmcneill static int
194 1.4 jmcneill au8522_set_if(struct au8522 *au)
195 1.4 jmcneill {
196 1.4 jmcneill uint8_t ifinit[3];
197 1.4 jmcneill unsigned int n;
198 1.4 jmcneill
199 1.4 jmcneill switch (au->if_freq) {
200 1.4 jmcneill case 6000000: /* 6MHz */
201 1.4 jmcneill ifinit[0] = 0xfb;
202 1.4 jmcneill ifinit[1] = 0x8e;
203 1.4 jmcneill ifinit[2] = 0x39;
204 1.4 jmcneill break;
205 1.4 jmcneill default:
206 1.4 jmcneill aprint_error_dev(au->parent, "au8522: unsupported if freq %dHz\n", au->if_freq);
207 1.4 jmcneill return EINVAL;
208 1.4 jmcneill }
209 1.4 jmcneill
210 1.4 jmcneill for (n = 0; n < __arraycount(ifinit); n++)
211 1.4 jmcneill au8522_write_1(au, 0x80b5 + n, ifinit[n]);
212 1.4 jmcneill
213 1.4 jmcneill return 0;
214 1.4 jmcneill }
215 1.4 jmcneill
216 1.1 jmcneill struct au8522 *
217 1.4 jmcneill au8522_open(device_t parent, i2c_tag_t i2c, i2c_addr_t addr, unsigned int if_freq)
218 1.1 jmcneill {
219 1.1 jmcneill struct au8522 *au;
220 1.1 jmcneill
221 1.1 jmcneill au = kmem_alloc(sizeof(*au), KM_SLEEP);
222 1.1 jmcneill if (au == NULL)
223 1.1 jmcneill return NULL;
224 1.1 jmcneill au->parent = parent;
225 1.1 jmcneill au->i2c = i2c;
226 1.1 jmcneill au->i2c_addr = addr;
227 1.4 jmcneill au->current_modulation = -1;
228 1.4 jmcneill au->if_freq = if_freq;
229 1.1 jmcneill
230 1.1 jmcneill if (au8522_reset(au))
231 1.1 jmcneill goto failed;
232 1.1 jmcneill if (au8522_write_1(au, AU8522_REG_TUNERCTL, AU8522_TUNERCTL_EN))
233 1.1 jmcneill goto failed;
234 1.1 jmcneill
235 1.1 jmcneill return au;
236 1.1 jmcneill
237 1.1 jmcneill failed:
238 1.1 jmcneill kmem_free(au, sizeof(*au));
239 1.1 jmcneill return NULL;
240 1.1 jmcneill }
241 1.1 jmcneill
242 1.1 jmcneill void
243 1.1 jmcneill au8522_close(struct au8522 *au)
244 1.1 jmcneill {
245 1.1 jmcneill kmem_free(au, sizeof(*au));
246 1.1 jmcneill }
247 1.1 jmcneill
248 1.1 jmcneill void
249 1.1 jmcneill au8522_enable(struct au8522 *au, bool enable)
250 1.1 jmcneill {
251 1.1 jmcneill if (enable) {
252 1.1 jmcneill au8522_write_1(au, AU8522_REG_SYSMODCTL0,
253 1.1 jmcneill AU8522_SYSMODCTL0_RESET);
254 1.1 jmcneill delay(1000);
255 1.1 jmcneill au8522_write_1(au, AU8522_REG_SYSMODCTL0,
256 1.1 jmcneill AU8522_SYSMODCTL0_CVBS);
257 1.1 jmcneill } else {
258 1.1 jmcneill au8522_write_1(au, AU8522_REG_SYSMODCTL0,
259 1.1 jmcneill AU8522_SYSMODCTL0_DISABLE);
260 1.1 jmcneill }
261 1.1 jmcneill }
262 1.1 jmcneill
263 1.1 jmcneill void
264 1.1 jmcneill au8522_set_input(struct au8522 *au, au8522_vinput_t vi, au8522_ainput_t ai)
265 1.1 jmcneill {
266 1.1 jmcneill au8522_reset(au);
267 1.1 jmcneill
268 1.1 jmcneill if (vi != AU8522_VINPUT_UNCONF)
269 1.1 jmcneill au8522_set_vinput(au, vi);
270 1.1 jmcneill if (ai != AU8522_AINPUT_UNCONF)
271 1.1 jmcneill au8522_set_ainput(au, ai);
272 1.1 jmcneill }
273 1.1 jmcneill
274 1.1 jmcneill int
275 1.1 jmcneill au8522_get_signal(struct au8522 *au)
276 1.1 jmcneill {
277 1.1 jmcneill uint8_t status;
278 1.1 jmcneill
279 1.1 jmcneill if (au8522_read_1(au, AU8522_REG_STATUS, &status))
280 1.1 jmcneill return 0;
281 1.1 jmcneill
282 1.1 jmcneill #ifdef AU8522_DEBUG
283 1.1 jmcneill printf("au8522: status=0x%02x\n", status);
284 1.1 jmcneill #endif
285 1.1 jmcneill return (status & AU8522_STATUS_LOCK) == AU8522_STATUS_LOCK ? 1 : 0;
286 1.1 jmcneill }
287 1.1 jmcneill
288 1.3 jmcneill void
289 1.3 jmcneill au8522_set_audio(struct au8522 *au, bool onoff)
290 1.3 jmcneill {
291 1.3 jmcneill if (onoff) {
292 1.3 jmcneill au8522_write_1(au, AU8522_REG_AUDIO_VOL_L, 0x7f);
293 1.3 jmcneill au8522_write_1(au, AU8522_REG_AUDIO_VOL_R, 0x7f);
294 1.3 jmcneill au8522_write_1(au, AU8522_REG_AUDIO_VOL, 0xff);
295 1.3 jmcneill } else {
296 1.3 jmcneill au8522_write_1(au, AU8522_REG_AUDIO_VOL_L, 0x00);
297 1.3 jmcneill au8522_write_1(au, AU8522_REG_AUDIO_VOL_R, 0x00);
298 1.3 jmcneill au8522_write_1(au, AU8522_REG_AUDIO_VOL, 0x00);
299 1.3 jmcneill }
300 1.3 jmcneill }
301 1.3 jmcneill
302 1.4 jmcneill int
303 1.4 jmcneill au8522_set_modulation(struct au8522 *au, fe_modulation_t modulation)
304 1.4 jmcneill {
305 1.4 jmcneill const struct au8522_modulation_table *modtab = NULL;
306 1.4 jmcneill size_t modtablen;
307 1.4 jmcneill unsigned int n;
308 1.4 jmcneill
309 1.4 jmcneill switch (modulation) {
310 1.4 jmcneill case VSB_8:
311 1.4 jmcneill modtab = au8522_modulation_8vsb;
312 1.4 jmcneill modtablen = __arraycount(au8522_modulation_8vsb);
313 1.4 jmcneill break;
314 1.4 jmcneill case QAM_64:
315 1.4 jmcneill modtab = au8522_modulation_qam64;
316 1.4 jmcneill modtablen = __arraycount(au8522_modulation_qam64);
317 1.4 jmcneill break;
318 1.4 jmcneill case QAM_256:
319 1.4 jmcneill modtab = au8522_modulation_qam256;
320 1.4 jmcneill modtablen = __arraycount(au8522_modulation_qam256);
321 1.4 jmcneill break;
322 1.4 jmcneill default:
323 1.4 jmcneill return EINVAL;
324 1.4 jmcneill }
325 1.4 jmcneill
326 1.4 jmcneill for (n = 0; n < modtablen; n++)
327 1.4 jmcneill au8522_write_1(au, modtab[n].reg, modtab[n].val);
328 1.4 jmcneill
329 1.4 jmcneill au8522_set_if(au);
330 1.4 jmcneill
331 1.4 jmcneill au->current_modulation = modulation;
332 1.4 jmcneill
333 1.4 jmcneill return 0;
334 1.4 jmcneill }
335 1.4 jmcneill
336 1.4 jmcneill void
337 1.4 jmcneill au8522_set_gate(struct au8522 *au, bool onoff)
338 1.4 jmcneill {
339 1.4 jmcneill au8522_write_1(au, AU8522_REG_TUNERCTL, onoff ? AU8522_TUNERCTL_EN : 0);
340 1.4 jmcneill }
341 1.4 jmcneill
342 1.4 jmcneill fe_status_t
343 1.4 jmcneill au8522_get_dtv_status(struct au8522 *au)
344 1.4 jmcneill {
345 1.4 jmcneill fe_status_t status = 0;
346 1.4 jmcneill uint8_t val;
347 1.4 jmcneill
348 1.4 jmcneill //printf("%s: current_modulation = %d\n", __func__,
349 1.4 jmcneill // au->current_modulation);
350 1.4 jmcneill
351 1.4 jmcneill switch (au->current_modulation) {
352 1.4 jmcneill case VSB_8:
353 1.4 jmcneill if (au8522_read_1(au, 0x4088, &val))
354 1.4 jmcneill return 0;
355 1.4 jmcneill if ((val & 0x03) == 0x03) {
356 1.4 jmcneill status |= FE_HAS_SIGNAL;
357 1.4 jmcneill status |= FE_HAS_CARRIER;
358 1.4 jmcneill status |= FE_HAS_VITERBI;
359 1.4 jmcneill }
360 1.4 jmcneill break;
361 1.4 jmcneill case QAM_64:
362 1.4 jmcneill case QAM_256:
363 1.4 jmcneill if (au8522_read_1(au, 0x4541, &val))
364 1.4 jmcneill return 0;
365 1.4 jmcneill if (val & 0x80) {
366 1.4 jmcneill status |= FE_HAS_VITERBI;
367 1.4 jmcneill }
368 1.4 jmcneill if (val & 0x20) {
369 1.4 jmcneill status |= FE_HAS_SIGNAL;
370 1.4 jmcneill status |= FE_HAS_CARRIER;
371 1.4 jmcneill }
372 1.4 jmcneill break;
373 1.4 jmcneill default:
374 1.4 jmcneill break;
375 1.4 jmcneill }
376 1.4 jmcneill
377 1.4 jmcneill if (status & FE_HAS_VITERBI) {
378 1.4 jmcneill status |= FE_HAS_SYNC;
379 1.4 jmcneill status |= FE_HAS_LOCK;
380 1.4 jmcneill }
381 1.4 jmcneill
382 1.4 jmcneill //printf("%s: status = 0x%x\n", __func__, status);
383 1.4 jmcneill return status;
384 1.4 jmcneill }
385 1.4 jmcneill
386 1.1 jmcneill MODULE(MODULE_CLASS_DRIVER, au8522, NULL);
387 1.1 jmcneill
388 1.1 jmcneill static int
389 1.1 jmcneill au8522_modcmd(modcmd_t cmd, void *opaque)
390 1.1 jmcneill {
391 1.1 jmcneill switch (cmd) {
392 1.1 jmcneill case MODULE_CMD_INIT:
393 1.1 jmcneill return 0;
394 1.1 jmcneill case MODULE_CMD_FINI:
395 1.1 jmcneill return 0;
396 1.1 jmcneill default:
397 1.1 jmcneill return ENOTTY;
398 1.1 jmcneill }
399 1.1 jmcneill }
400