1 /* $NetBSD: au8522.c,v 1.2 2010/12/28 00:11:50 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.2 2010/12/28 00:11:50 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/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 MODULE(MODULE_CLASS_DRIVER, au8522, NULL); 261 262 static int 263 au8522_modcmd(modcmd_t cmd, void *opaque) 264 { 265 switch (cmd) { 266 case MODULE_CMD_INIT: 267 return 0; 268 case MODULE_CMD_FINI: 269 return 0; 270 default: 271 return ENOTTY; 272 } 273 } 274