1 /* $NetBSD: au8522.c,v 1.8 2017/06/01 02:45:10 chs 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.8 2017/06/01 02:45:10 chs 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 au->parent = parent; 223 au->i2c = i2c; 224 au->i2c_addr = addr; 225 au->current_modulation = -1; 226 au->if_freq = if_freq; 227 228 if (au8522_reset(au)) 229 goto failed; 230 if (au8522_write_1(au, AU8522_REG_TUNERCTL, AU8522_TUNERCTL_EN)) 231 goto failed; 232 233 return au; 234 235 failed: 236 kmem_free(au, sizeof(*au)); 237 return NULL; 238 } 239 240 void 241 au8522_close(struct au8522 *au) 242 { 243 kmem_free(au, sizeof(*au)); 244 } 245 246 void 247 au8522_enable(struct au8522 *au, bool enable) 248 { 249 if (enable) { 250 au8522_write_1(au, AU8522_REG_SYSMODCTL0, 251 AU8522_SYSMODCTL0_RESET); 252 delay(1000); 253 au8522_write_1(au, AU8522_REG_SYSMODCTL0, 254 AU8522_SYSMODCTL0_CVBS); 255 } else { 256 au8522_write_1(au, AU8522_REG_SYSMODCTL0, 257 AU8522_SYSMODCTL0_DISABLE); 258 } 259 } 260 261 void 262 au8522_set_input(struct au8522 *au, au8522_vinput_t vi, au8522_ainput_t ai) 263 { 264 au8522_reset(au); 265 266 if (vi != AU8522_VINPUT_UNCONF) 267 au8522_set_vinput(au, vi); 268 if (ai != AU8522_AINPUT_UNCONF) 269 au8522_set_ainput(au, ai); 270 } 271 272 int 273 au8522_get_signal(struct au8522 *au) 274 { 275 uint8_t status; 276 277 if (au8522_read_1(au, AU8522_REG_STATUS, &status)) 278 return 0; 279 280 #ifdef AU8522_DEBUG 281 printf("au8522: status=0x%02x\n", status); 282 #endif 283 return (status & AU8522_STATUS_LOCK) == AU8522_STATUS_LOCK ? 1 : 0; 284 } 285 286 void 287 au8522_set_audio(struct au8522 *au, bool onoff) 288 { 289 if (onoff) { 290 au8522_write_1(au, AU8522_REG_AUDIO_VOL_L, 0x7f); 291 au8522_write_1(au, AU8522_REG_AUDIO_VOL_R, 0x7f); 292 au8522_write_1(au, AU8522_REG_AUDIO_VOL, 0xff); 293 } else { 294 au8522_write_1(au, AU8522_REG_AUDIO_VOL_L, 0x00); 295 au8522_write_1(au, AU8522_REG_AUDIO_VOL_R, 0x00); 296 au8522_write_1(au, AU8522_REG_AUDIO_VOL, 0x00); 297 } 298 } 299 300 int 301 au8522_set_modulation(struct au8522 *au, fe_modulation_t modulation) 302 { 303 const struct au8522_modulation_table *modtab = NULL; 304 size_t modtablen; 305 unsigned int n; 306 307 switch (modulation) { 308 case VSB_8: 309 modtab = au8522_modulation_8vsb; 310 modtablen = __arraycount(au8522_modulation_8vsb); 311 break; 312 case QAM_64: 313 modtab = au8522_modulation_qam64; 314 modtablen = __arraycount(au8522_modulation_qam64); 315 break; 316 case QAM_256: 317 modtab = au8522_modulation_qam256; 318 modtablen = __arraycount(au8522_modulation_qam256); 319 break; 320 default: 321 return EINVAL; 322 } 323 324 for (n = 0; n < modtablen; n++) 325 au8522_write_1(au, modtab[n].reg, modtab[n].val); 326 327 au8522_set_if(au); 328 329 au->current_modulation = modulation; 330 331 return 0; 332 } 333 334 void 335 au8522_set_gate(struct au8522 *au, bool onoff) 336 { 337 au8522_write_1(au, AU8522_REG_TUNERCTL, onoff ? AU8522_TUNERCTL_EN : 0); 338 } 339 340 fe_status_t 341 au8522_get_dtv_status(struct au8522 *au) 342 { 343 fe_status_t status = 0; 344 uint8_t val; 345 346 switch (au->current_modulation) { 347 case VSB_8: 348 if (au8522_read_1(au, 0x4088, &val)) 349 return 0; 350 if ((val & 0x03) == 0x03) { 351 status |= FE_HAS_SIGNAL; 352 status |= FE_HAS_CARRIER; 353 status |= FE_HAS_VITERBI; 354 } 355 break; 356 case QAM_64: 357 case QAM_256: 358 if (au8522_read_1(au, 0x4541, &val)) 359 return 0; 360 if (val & 0x80) { 361 status |= FE_HAS_VITERBI; 362 } 363 if (val & 0x20) { 364 status |= FE_HAS_SIGNAL; 365 status |= FE_HAS_CARRIER; 366 } 367 break; 368 default: 369 break; 370 } 371 372 if (status & FE_HAS_VITERBI) { 373 status |= FE_HAS_SYNC; 374 status |= FE_HAS_LOCK; 375 } 376 377 return status; 378 } 379 380 uint16_t 381 au8522_get_snr(struct au8522 *au) 382 { 383 const struct au8522_snr_table *snrtab = NULL; 384 uint16_t snrreg; 385 uint8_t val; 386 size_t snrtablen; 387 unsigned int n; 388 389 switch (au->current_modulation) { 390 case VSB_8: 391 snrtab = au8522_snr_8vsb; 392 snrtablen = __arraycount(au8522_snr_8vsb); 393 snrreg = AU8522_REG_SNR_VSB; 394 break; 395 case QAM_64: 396 snrtab = au8522_snr_qam64; 397 snrtablen = __arraycount(au8522_snr_qam64); 398 snrreg = AU8522_REG_SNR_QAM; 399 break; 400 case QAM_256: 401 snrtab = au8522_snr_qam256; 402 snrtablen = __arraycount(au8522_snr_qam256); 403 snrreg = AU8522_REG_SNR_QAM; 404 break; 405 default: 406 return 0; 407 } 408 409 if (au8522_read_1(au, snrreg, &val)) 410 return 0; 411 412 for (n = 0; n < snrtablen; n++) { 413 if (val < snrtab[n].val) 414 return snrtab[n].snr; 415 } 416 417 return 0; 418 } 419 420 MODULE(MODULE_CLASS_DRIVER, au8522, "i2cexec"); 421 422 static int 423 au8522_modcmd(modcmd_t cmd, void *opaque) 424 { 425 switch (cmd) { 426 case MODULE_CMD_INIT: 427 return 0; 428 case MODULE_CMD_FINI: 429 return 0; 430 default: 431 return ENOTTY; 432 } 433 } 434