1 /* $NetBSD: au8522.c,v 1.7 2015/03/07 14:16:51 jmcneill Exp $ */ 2 3 /*- 4 * Copyright (c) 2010 Jared D. McNeill <jmcneill@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.7 2015/03/07 14:16:51 jmcneill 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/dtv/dtvio.h> 45 46 #include <dev/i2c/i2cvar.h> 47 48 #include <dev/i2c/au8522reg.h> 49 #include <dev/i2c/au8522var.h> 50 #include <dev/i2c/au8522mod.h> 51 52 static int au8522_reset(struct au8522 *); 53 static int au8522_read_1(struct au8522 *, uint16_t, uint8_t *); 54 static int au8522_write_1(struct au8522 *, uint16_t, uint8_t); 55 static int au8522_set_vinput(struct au8522 *, au8522_vinput_t); 56 static int au8522_set_ainput(struct au8522 *, au8522_ainput_t); 57 static void au8522_set_common(struct au8522 *, au8522_vinput_t); 58 59 static int 60 au8522_reset(struct au8522 *au) 61 { 62 return au8522_write_1(au, 0xa4, 1 << 5); 63 } 64 65 static int 66 au8522_read_1(struct au8522 *au, uint16_t reg, uint8_t *val) 67 { 68 uint8_t cmd[2]; 69 int error; 70 71 cmd[0] = (reg >> 8) | 0x40; 72 cmd[1] = reg & 0xff; 73 error = iic_exec(au->i2c, I2C_OP_WRITE, au->i2c_addr, 74 cmd, sizeof(cmd), NULL, 0, 0); 75 if (error) 76 return error; 77 return iic_exec(au->i2c, I2C_OP_READ, au->i2c_addr, 78 NULL, 0, val, sizeof(*val), 0); 79 } 80 81 static int 82 au8522_write_1(struct au8522 *au, uint16_t reg, uint8_t val) 83 { 84 uint8_t data[3]; 85 86 data[0] = (reg >> 8) | 0x80; 87 data[1] = reg & 0xff; 88 data[2] = val; 89 return iic_exec(au->i2c, I2C_OP_WRITE, au->i2c_addr, 90 data, sizeof(data), NULL, 0, 0); 91 } 92 93 static int 94 au8522_set_vinput(struct au8522 *au, au8522_vinput_t vi) 95 { 96 switch (vi) { 97 case AU8522_VINPUT_CVBS: 98 au8522_write_1(au, AU8522_REG_MODCLKCTL, AU8522_MODCLKCTL_CVBS); 99 au8522_write_1(au, AU8522_REG_PGACTL, 0x00); 100 au8522_write_1(au, AU8522_REG_CLAMPCTL, 0x0e); 101 au8522_write_1(au, AU8522_REG_PGACTL, 0x10); 102 au8522_write_1(au, AU8522_REG_INPUTCTL, 103 AU8522_INPUTCTL_CVBS_CH1); 104 105 au8522_set_common(au, vi); 106 107 au8522_write_1(au, AU8522_REG_SYSMODCTL0, 108 AU8522_SYSMODCTL0_CVBS); 109 break; 110 case AU8522_VINPUT_SVIDEO: 111 au8522_write_1(au, AU8522_REG_MODCLKCTL, 112 AU8522_MODCLKCTL_SVIDEO); 113 au8522_write_1(au, AU8522_REG_INPUTCTL, 114 AU8522_INPUTCTL_SVIDEO_CH13); 115 au8522_write_1(au, AU8522_REG_CLAMPCTL, 0x00); 116 117 au8522_set_common(au, vi); 118 119 au8522_write_1(au, AU8522_REG_SYSMODCTL0, 120 AU8522_SYSMODCTL0_CVBS); 121 122 break; 123 case AU8522_VINPUT_CVBS_TUNER: 124 au8522_write_1(au, AU8522_REG_MODCLKCTL, AU8522_MODCLKCTL_CVBS); 125 au8522_write_1(au, AU8522_REG_PGACTL, 0x00); 126 au8522_write_1(au, AU8522_REG_CLAMPCTL, 0x0e); 127 au8522_write_1(au, AU8522_REG_PGACTL, 0x10); 128 au8522_write_1(au, AU8522_REG_INPUTCTL, 129 AU8522_INPUTCTL_CVBS_CH4_SIF); 130 131 au8522_set_common(au, vi); 132 133 au8522_write_1(au, AU8522_REG_SYSMODCTL0, 134 AU8522_SYSMODCTL0_CVBS); 135 136 break; 137 default: 138 return EINVAL; 139 } 140 141 return 0; 142 } 143 144 static void 145 au8522_set_common(struct au8522 *au, au8522_vinput_t vi) 146 { 147 au8522_write_1(au, AU8522_REG_INTMASK, 0x00); 148 au8522_write_1(au, AU8522_REG_VIDEOMODE, vi == AU8522_VINPUT_SVIDEO ? 149 AU8522_VIDEOMODE_SVIDEO : AU8522_VIDEOMODE_CVBS); 150 au8522_write_1(au, AU8522_REG_TV_PGA, AU8522_TV_PGA_CVBS); 151 } 152 153 static int 154 au8522_set_ainput(struct au8522 *au, au8522_ainput_t ai) 155 { 156 /* mute during mode change */ 157 au8522_write_1(au, AU8522_REG_AUDIO_VOL_L, 0x00); 158 au8522_write_1(au, AU8522_REG_AUDIO_VOL_R, 0x00); 159 au8522_write_1(au, AU8522_REG_AUDIO_VOL, 0x00); 160 161 switch (ai) { 162 case AU8522_AINPUT_SIF: 163 au8522_write_1(au, AU8522_REG_SYSMODCTL0, 164 AU8522_SYSMODCTL0_CVBS); 165 au8522_write_1(au, AU8522_REG_AUDIO_MODE, 0x82); 166 au8522_write_1(au, AU8522_REG_SYSMODCTL1, 167 AU8522_SYSMODCTL1_I2S); 168 au8522_write_1(au, AU8522_REG_AUDIO_FREQ, 0x03); 169 au8522_write_1(au, AU8522_REG_I2S_CTL2, 0xc2); 170 /* unmute */ 171 au8522_write_1(au, AU8522_REG_AUDIO_VOL_L, 0x7f); 172 au8522_write_1(au, AU8522_REG_AUDIO_VOL_R, 0x7f); 173 au8522_write_1(au, AU8522_REG_AUDIO_VOL, 0xff); 174 break; 175 case AU8522_AINPUT_NONE: 176 au8522_write_1(au, AU8522_REG_USBEN, 0x00); 177 au8522_write_1(au, AU8522_REG_AUDIO_VOL_L, 0x7f); 178 au8522_write_1(au, AU8522_REG_AUDIO_VOL_R, 0x7f); 179 au8522_write_1(au, AU8522_REG_AUDIO_MODE, 0x40); 180 au8522_write_1(au, AU8522_REG_SYSMODCTL1, 181 AU8522_SYSMODCTL1_SVIDEO); 182 au8522_write_1(au, AU8522_REG_AUDIO_FREQ, 0x03); 183 au8522_write_1(au, AU8522_REG_I2S_CTL2, 0x02); 184 au8522_write_1(au, AU8522_REG_SYSMODCTL0, 185 AU8522_SYSMODCTL0_CVBS); 186 break; 187 default: 188 return EINVAL; 189 } 190 return 0; 191 } 192 193 static int 194 au8522_set_if(struct au8522 *au) 195 { 196 uint8_t ifinit[3]; 197 unsigned int n; 198 199 switch (au->if_freq) { 200 case 6000000: /* 6MHz */ 201 ifinit[0] = 0xfb; 202 ifinit[1] = 0x8e; 203 ifinit[2] = 0x39; 204 break; 205 default: 206 aprint_error_dev(au->parent, "au8522: unsupported if freq %dHz\n", au->if_freq); 207 return EINVAL; 208 } 209 210 for (n = 0; n < __arraycount(ifinit); n++) 211 au8522_write_1(au, 0x80b5 + n, ifinit[n]); 212 213 return 0; 214 } 215 216 struct au8522 * 217 au8522_open(device_t parent, i2c_tag_t i2c, i2c_addr_t addr, unsigned int if_freq) 218 { 219 struct au8522 *au; 220 221 au = kmem_alloc(sizeof(*au), KM_SLEEP); 222 if (au == NULL) 223 return NULL; 224 au->parent = parent; 225 au->i2c = i2c; 226 au->i2c_addr = addr; 227 au->current_modulation = -1; 228 au->if_freq = if_freq; 229 230 if (au8522_reset(au)) 231 goto failed; 232 if (au8522_write_1(au, AU8522_REG_TUNERCTL, AU8522_TUNERCTL_EN)) 233 goto failed; 234 235 return au; 236 237 failed: 238 kmem_free(au, sizeof(*au)); 239 return NULL; 240 } 241 242 void 243 au8522_close(struct au8522 *au) 244 { 245 kmem_free(au, sizeof(*au)); 246 } 247 248 void 249 au8522_enable(struct au8522 *au, bool enable) 250 { 251 if (enable) { 252 au8522_write_1(au, AU8522_REG_SYSMODCTL0, 253 AU8522_SYSMODCTL0_RESET); 254 delay(1000); 255 au8522_write_1(au, AU8522_REG_SYSMODCTL0, 256 AU8522_SYSMODCTL0_CVBS); 257 } else { 258 au8522_write_1(au, AU8522_REG_SYSMODCTL0, 259 AU8522_SYSMODCTL0_DISABLE); 260 } 261 } 262 263 void 264 au8522_set_input(struct au8522 *au, au8522_vinput_t vi, au8522_ainput_t ai) 265 { 266 au8522_reset(au); 267 268 if (vi != AU8522_VINPUT_UNCONF) 269 au8522_set_vinput(au, vi); 270 if (ai != AU8522_AINPUT_UNCONF) 271 au8522_set_ainput(au, ai); 272 } 273 274 int 275 au8522_get_signal(struct au8522 *au) 276 { 277 uint8_t status; 278 279 if (au8522_read_1(au, AU8522_REG_STATUS, &status)) 280 return 0; 281 282 #ifdef AU8522_DEBUG 283 printf("au8522: status=0x%02x\n", status); 284 #endif 285 return (status & AU8522_STATUS_LOCK) == AU8522_STATUS_LOCK ? 1 : 0; 286 } 287 288 void 289 au8522_set_audio(struct au8522 *au, bool onoff) 290 { 291 if (onoff) { 292 au8522_write_1(au, AU8522_REG_AUDIO_VOL_L, 0x7f); 293 au8522_write_1(au, AU8522_REG_AUDIO_VOL_R, 0x7f); 294 au8522_write_1(au, AU8522_REG_AUDIO_VOL, 0xff); 295 } else { 296 au8522_write_1(au, AU8522_REG_AUDIO_VOL_L, 0x00); 297 au8522_write_1(au, AU8522_REG_AUDIO_VOL_R, 0x00); 298 au8522_write_1(au, AU8522_REG_AUDIO_VOL, 0x00); 299 } 300 } 301 302 int 303 au8522_set_modulation(struct au8522 *au, fe_modulation_t modulation) 304 { 305 const struct au8522_modulation_table *modtab = NULL; 306 size_t modtablen; 307 unsigned int n; 308 309 switch (modulation) { 310 case VSB_8: 311 modtab = au8522_modulation_8vsb; 312 modtablen = __arraycount(au8522_modulation_8vsb); 313 break; 314 case QAM_64: 315 modtab = au8522_modulation_qam64; 316 modtablen = __arraycount(au8522_modulation_qam64); 317 break; 318 case QAM_256: 319 modtab = au8522_modulation_qam256; 320 modtablen = __arraycount(au8522_modulation_qam256); 321 break; 322 default: 323 return EINVAL; 324 } 325 326 for (n = 0; n < modtablen; n++) 327 au8522_write_1(au, modtab[n].reg, modtab[n].val); 328 329 au8522_set_if(au); 330 331 au->current_modulation = modulation; 332 333 return 0; 334 } 335 336 void 337 au8522_set_gate(struct au8522 *au, bool onoff) 338 { 339 au8522_write_1(au, AU8522_REG_TUNERCTL, onoff ? AU8522_TUNERCTL_EN : 0); 340 } 341 342 fe_status_t 343 au8522_get_dtv_status(struct au8522 *au) 344 { 345 fe_status_t status = 0; 346 uint8_t val; 347 348 switch (au->current_modulation) { 349 case VSB_8: 350 if (au8522_read_1(au, 0x4088, &val)) 351 return 0; 352 if ((val & 0x03) == 0x03) { 353 status |= FE_HAS_SIGNAL; 354 status |= FE_HAS_CARRIER; 355 status |= FE_HAS_VITERBI; 356 } 357 break; 358 case QAM_64: 359 case QAM_256: 360 if (au8522_read_1(au, 0x4541, &val)) 361 return 0; 362 if (val & 0x80) { 363 status |= FE_HAS_VITERBI; 364 } 365 if (val & 0x20) { 366 status |= FE_HAS_SIGNAL; 367 status |= FE_HAS_CARRIER; 368 } 369 break; 370 default: 371 break; 372 } 373 374 if (status & FE_HAS_VITERBI) { 375 status |= FE_HAS_SYNC; 376 status |= FE_HAS_LOCK; 377 } 378 379 return status; 380 } 381 382 uint16_t 383 au8522_get_snr(struct au8522 *au) 384 { 385 const struct au8522_snr_table *snrtab = NULL; 386 uint16_t snrreg; 387 uint8_t val; 388 size_t snrtablen; 389 unsigned int n; 390 391 switch (au->current_modulation) { 392 case VSB_8: 393 snrtab = au8522_snr_8vsb; 394 snrtablen = __arraycount(au8522_snr_8vsb); 395 snrreg = AU8522_REG_SNR_VSB; 396 break; 397 case QAM_64: 398 snrtab = au8522_snr_qam64; 399 snrtablen = __arraycount(au8522_snr_qam64); 400 snrreg = AU8522_REG_SNR_QAM; 401 break; 402 case QAM_256: 403 snrtab = au8522_snr_qam256; 404 snrtablen = __arraycount(au8522_snr_qam256); 405 snrreg = AU8522_REG_SNR_QAM; 406 break; 407 default: 408 return 0; 409 } 410 411 if (au8522_read_1(au, snrreg, &val)) 412 return 0; 413 414 for (n = 0; n < snrtablen; n++) { 415 if (val < snrtab[n].val) 416 return snrtab[n].snr; 417 } 418 419 return 0; 420 } 421 422 MODULE(MODULE_CLASS_DRIVER, au8522, "i2cexec"); 423 424 static int 425 au8522_modcmd(modcmd_t cmd, void *opaque) 426 { 427 switch (cmd) { 428 case MODULE_CMD_INIT: 429 return 0; 430 case MODULE_CMD_FINI: 431 return 0; 432 default: 433 return ENOTTY; 434 } 435 } 436