Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

V4L/DVB (3661): Add wm8739 stereo audio ADC i2c driver

Add support for the Wolfson Microelectronics WM8739
stereo A/D converter from the ivtv driver.
Many thanks to Takahiro Adachi for writing the original driver.

Signed-off-by: Takahiro Adachi <tadachi@tadachi-net.com>
Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>

authored by

Hans Verkuil and committed by
Mauro Carvalho Chehab
75c4570c 953a676c

+369 -3
+13 -3
drivers/media/video/Kconfig
··· 343 343 module will be called cs53l32a 344 344 345 345 config VIDEO_WM8775 346 - tristate "Wolfson Microelectronics WM8775 audio ADC" 346 + tristate "Wolfson Microelectronics WM8775 audio ADC with input mixer" 347 347 depends on VIDEO_DEV && I2C && EXPERIMENTAL 348 348 ---help--- 349 - Support for the Wolfson Microelectronics WM8775 350 - high performance stereo A/D Converter. 349 + Support for the Wolfson Microelectronics WM8775 high 350 + performance stereo A/D Converter with a 4 channel input mixer. 351 351 352 352 To compile this driver as a module, choose M here: the 353 353 module will be called wm8775 354 + 355 + config VIDEO_WM8739 356 + tristate "Wolfson Microelectronics WM8739 stereo audio ADC" 357 + depends on VIDEO_DEV && I2C && EXPERIMENTAL 358 + ---help--- 359 + Support for the Wolfson Microelectronics WM8739 360 + stereo A/D Converter. 361 + 362 + To compile this driver as a module, choose M here: the 363 + module will be called wm8739 354 364 355 365 source "drivers/media/video/cx25840/Kconfig" 356 366
+1
drivers/media/video/Makefile
··· 45 45 obj-$(CONFIG_VIDEO_MSP3400) += msp3400.o 46 46 obj-$(CONFIG_VIDEO_CS53L32A) += cs53l32a.o 47 47 obj-$(CONFIG_VIDEO_WM8775) += wm8775.o 48 + obj-$(CONFIG_VIDEO_WM8739) += wm8739.o 48 49 obj-$(CONFIG_VIDEO_OVCAMCHIP) += ovcamchip/ 49 50 obj-$(CONFIG_VIDEO_CPIA2) += cpia2/ 50 51 obj-$(CONFIG_VIDEO_MXB) += saa7111.o tda9840.o tea6415c.o tea6420.o mxb.o
+355
drivers/media/video/wm8739.c
··· 1 + /* 2 + * wm8739 3 + * 4 + * Copyright (C) 2005 T. Adachi <tadachi@tadachi-net.com> 5 + * 6 + * Copyright (C) 2005 Hans Verkuil <hverkuil@xs4all.nl> 7 + * - Cleanup 8 + * 9 + * This program is free software; you can redistribute it and/or modify 10 + * it under the terms of the GNU General Public License as published by 11 + * the Free Software Foundation; either version 2 of the License, or 12 + * (at your option) any later version. 13 + * 14 + * This program is distributed in the hope that it will be useful, 15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 + * GNU General Public License for more details. 18 + * 19 + * You should have received a copy of the GNU General Public License 20 + * along with this program; if not, write to the Free Software 21 + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 22 + */ 23 + 24 + #include <linux/module.h> 25 + #include <linux/types.h> 26 + #include <linux/ioctl.h> 27 + #include <asm/uaccess.h> 28 + #include <linux/i2c.h> 29 + #include <linux/i2c-id.h> 30 + #include <linux/videodev.h> 31 + #include <media/v4l2-common.h> 32 + 33 + MODULE_DESCRIPTION("wm8739 driver"); 34 + MODULE_AUTHOR("T. Adachi, Hans Verkuil"); 35 + MODULE_LICENSE("GPL"); 36 + 37 + static int debug = 0; 38 + static unsigned short normal_i2c[] = { 0x34 >> 1, 0x36 >> 1, I2C_CLIENT_END }; 39 + 40 + module_param(debug, int, 0644); 41 + 42 + MODULE_PARM_DESC(debug, "Debug level (0-1)"); 43 + 44 + 45 + I2C_CLIENT_INSMOD; 46 + 47 + /* ------------------------------------------------------------------------ */ 48 + 49 + enum { 50 + R0 = 0, R1, 51 + R5 = 5, R6, R7, R8, R9, R15 = 15, 52 + TOT_REGS 53 + }; 54 + 55 + struct wm8739_state { 56 + u32 clock_freq; 57 + u8 muted; 58 + u16 volume; 59 + u16 balance; 60 + u8 vol_l; /* +12dB to -34.5dB 1.5dB step (5bit) def:0dB */ 61 + u8 vol_r; /* +12dB to -34.5dB 1.5dB step (5bit) def:0dB */ 62 + }; 63 + 64 + /* ------------------------------------------------------------------------ */ 65 + 66 + static int wm8739_write(struct i2c_client *client, int reg, u16 val) 67 + { 68 + int i; 69 + 70 + if (reg < 0 || reg >= TOT_REGS) { 71 + v4l_err(client, "Invalid register R%d\n", reg); 72 + return -1; 73 + } 74 + 75 + v4l_dbg(1, debug, client, "write: %02x %02x\n", reg, val); 76 + 77 + for (i = 0; i < 3; i++) { 78 + if (i2c_smbus_write_byte_data(client, (reg << 1) | 79 + (val >> 8), val & 0xff) == 0) { 80 + return 0; 81 + } 82 + } 83 + v4l_err(client, "I2C: cannot write %03x to register R%d\n", val, reg); 84 + return -1; 85 + } 86 + 87 + /* write regs to set audio volume etc */ 88 + static void wm8739_set_audio(struct i2c_client *client) 89 + { 90 + struct wm8739_state *state = i2c_get_clientdata(client); 91 + u16 mute = state->muted ? 0x80 : 0; 92 + 93 + /* Volume setting: bits 0-4, 0x1f = 12 dB, 0x00 = -34.5 dB 94 + * Default setting: 0x17 = 0 dB 95 + */ 96 + wm8739_write(client, R0, (state->vol_l & 0x1f) | mute); 97 + wm8739_write(client, R1, (state->vol_r & 0x1f) | mute); 98 + } 99 + 100 + static int wm8739_get_ctrl(struct i2c_client *client, struct v4l2_control *ctrl) 101 + { 102 + struct wm8739_state *state = i2c_get_clientdata(client); 103 + 104 + switch (ctrl->id) { 105 + case V4L2_CID_AUDIO_MUTE: 106 + ctrl->value = state->muted; 107 + break; 108 + 109 + case V4L2_CID_AUDIO_VOLUME: 110 + ctrl->value = state->volume; 111 + break; 112 + 113 + case V4L2_CID_AUDIO_BALANCE: 114 + ctrl->value = state->balance; 115 + break; 116 + 117 + default: 118 + return -EINVAL; 119 + } 120 + return 0; 121 + } 122 + 123 + static int wm8739_set_ctrl(struct i2c_client *client, struct v4l2_control *ctrl) 124 + { 125 + struct wm8739_state *state = i2c_get_clientdata(client); 126 + unsigned int work_l, work_r; 127 + 128 + switch (ctrl->id) { 129 + case V4L2_CID_AUDIO_MUTE: 130 + state->muted = ctrl->value; 131 + break; 132 + 133 + case V4L2_CID_AUDIO_VOLUME: 134 + state->volume = ctrl->value; 135 + break; 136 + 137 + case V4L2_CID_AUDIO_BALANCE: 138 + state->balance = ctrl->value; 139 + break; 140 + 141 + default: 142 + return -EINVAL; 143 + } 144 + 145 + /* normalize ( 65535 to 0 -> 31 to 0 (12dB to -34.5dB) ) */ 146 + work_l = (min(65536 - state->balance, 32768) * state->volume) / 32768; 147 + work_r = (min(state->balance, (u16)32768) * state->volume) / 32768; 148 + 149 + state->vol_l = (long)work_l * 31 / 65535; 150 + state->vol_r = (long)work_r * 31 / 65535; 151 + 152 + /* set audio volume etc. */ 153 + wm8739_set_audio(client); 154 + return 0; 155 + } 156 + 157 + /* ------------------------------------------------------------------------ */ 158 + 159 + static struct v4l2_queryctrl wm8739_qctrl[] = { 160 + { 161 + .id = V4L2_CID_AUDIO_VOLUME, 162 + .name = "Volume", 163 + .minimum = 0, 164 + .maximum = 65535, 165 + .step = 65535/100, 166 + .default_value = 58880, 167 + .flags = 0, 168 + .type = V4L2_CTRL_TYPE_INTEGER, 169 + },{ 170 + .id = V4L2_CID_AUDIO_MUTE, 171 + .name = "Mute", 172 + .minimum = 0, 173 + .maximum = 1, 174 + .step = 1, 175 + .default_value = 1, 176 + .flags = 0, 177 + .type = V4L2_CTRL_TYPE_BOOLEAN, 178 + },{ 179 + .id = V4L2_CID_AUDIO_BALANCE, 180 + .name = "Balance", 181 + .minimum = 0, 182 + .maximum = 65535, 183 + .step = 65535/100, 184 + .default_value = 32768, 185 + .flags = 0, 186 + .type = V4L2_CTRL_TYPE_INTEGER, 187 + } 188 + }; 189 + 190 + /* ------------------------------------------------------------------------ */ 191 + 192 + static int wm8739_command(struct i2c_client *client, unsigned int cmd, void *arg) 193 + { 194 + struct wm8739_state *state = i2c_get_clientdata(client); 195 + 196 + switch (cmd) { 197 + case VIDIOC_INT_AUDIO_CLOCK_FREQ: 198 + { 199 + u32 audiofreq = *(u32 *)arg; 200 + 201 + state->clock_freq = audiofreq; 202 + wm8739_write(client, R9, 0x000); /* de-activate */ 203 + switch (audiofreq) { 204 + case 44100: 205 + wm8739_write(client, R8, 0x020); /* 256fps, fs=44.1k */ 206 + break; 207 + case 48000: 208 + wm8739_write(client, R8, 0x000); /* 256fps, fs=48k */ 209 + break; 210 + case 32000: 211 + wm8739_write(client, R8, 0x018); /* 256fps, fs=32k */ 212 + break; 213 + default: 214 + break; 215 + } 216 + wm8739_write(client, R9, 0x001); /* activate */ 217 + break; 218 + } 219 + 220 + case VIDIOC_G_CTRL: 221 + return wm8739_get_ctrl(client, arg); 222 + 223 + case VIDIOC_S_CTRL: 224 + return wm8739_set_ctrl(client, arg); 225 + 226 + case VIDIOC_QUERYCTRL: 227 + { 228 + struct v4l2_queryctrl *qc = arg; 229 + int i; 230 + 231 + for (i = 0; i < ARRAY_SIZE(wm8739_qctrl); i++) 232 + if (qc->id && qc->id == wm8739_qctrl[i].id) { 233 + memcpy(qc, &wm8739_qctrl[i], sizeof(*qc)); 234 + return 0; 235 + } 236 + return -EINVAL; 237 + } 238 + 239 + case VIDIOC_LOG_STATUS: 240 + v4l_info(client, "Frequency: %u Hz\n", state->clock_freq); 241 + v4l_info(client, "Volume L: %02x%s\n", state->vol_l & 0x1f, 242 + state->muted ? " (muted)" : ""); 243 + v4l_info(client, "Volume R: %02x%s\n", state->vol_r & 0x1f, 244 + state->muted ? " (muted)" : ""); 245 + break; 246 + 247 + default: 248 + return -EINVAL; 249 + } 250 + 251 + return 0; 252 + } 253 + 254 + /* ------------------------------------------------------------------------ */ 255 + 256 + /* i2c implementation */ 257 + 258 + static struct i2c_driver i2c_driver; 259 + 260 + static int wm8739_attach(struct i2c_adapter *adapter, int address, int kind) 261 + { 262 + struct i2c_client *client; 263 + struct wm8739_state *state; 264 + 265 + /* Check if the adapter supports the needed features */ 266 + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) 267 + return 0; 268 + 269 + client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); 270 + if (client == NULL) 271 + return -ENOMEM; 272 + 273 + client->addr = address; 274 + client->adapter = adapter; 275 + client->driver = &i2c_driver; 276 + snprintf(client->name, sizeof(client->name) - 1, "wm8739"); 277 + 278 + v4l_info(client, "chip found @ 0x%x (%s)\n", address << 1, adapter->name); 279 + 280 + state = kmalloc(sizeof(struct wm8739_state), GFP_KERNEL); 281 + if (state == NULL) { 282 + kfree(client); 283 + return -ENOMEM; 284 + } 285 + state->vol_l = 0x17; /* 0dB */ 286 + state->vol_r = 0x17; /* 0dB */ 287 + state->muted = 0; 288 + state->balance = 32768; 289 + /* normalize (12dB(31) to -34.5dB(0) [0dB(23)] -> 65535 to 0) */ 290 + state->volume = ((long)state->vol_l + 1) * 65535 / 31; 291 + state->clock_freq = 48000; 292 + i2c_set_clientdata(client, state); 293 + 294 + /* initialize wm8739 */ 295 + wm8739_write(client, R15, 0x00); /* reset */ 296 + wm8739_write(client, R5, 0x000); /* filter setting, high path, offet clear */ 297 + wm8739_write(client, R6, 0x000); /* ADC, OSC, Power Off mode Disable */ 298 + wm8739_write(client, R7, 0x049); /* Digital Audio interface format */ 299 + /* Enable Master mode */ 300 + /* 24 bit, MSB first/left justified */ 301 + wm8739_write(client, R8, 0x000); /* sampling control */ 302 + /* normal, 256fs, 48KHz sampling rate */ 303 + wm8739_write(client, R9, 0x001); /* activate */ 304 + wm8739_set_audio(client); /* set volume/mute */ 305 + 306 + i2c_attach_client(client); 307 + 308 + return 0; 309 + } 310 + 311 + static int wm8739_probe(struct i2c_adapter *adapter) 312 + { 313 + if (adapter->class & I2C_CLASS_TV_ANALOG) 314 + return i2c_probe(adapter, &addr_data, wm8739_attach); 315 + return 0; 316 + } 317 + 318 + static int wm8739_detach(struct i2c_client *client) 319 + { 320 + int err; 321 + 322 + err = i2c_detach_client(client); 323 + if (err) 324 + return err; 325 + 326 + kfree(client); 327 + return 0; 328 + } 329 + 330 + /* ----------------------------------------------------------------------- */ 331 + 332 + /* i2c implementation */ 333 + static struct i2c_driver i2c_driver = { 334 + .driver = { 335 + .name = "wm8739", 336 + }, 337 + .id = I2C_DRIVERID_WM8739, 338 + .attach_adapter = wm8739_probe, 339 + .detach_client = wm8739_detach, 340 + .command = wm8739_command, 341 + }; 342 + 343 + 344 + static int __init wm8739_init_module(void) 345 + { 346 + return i2c_add_driver(&i2c_driver); 347 + } 348 + 349 + static void __exit wm8739_cleanup_module(void) 350 + { 351 + i2c_del_driver(&i2c_driver); 352 + } 353 + 354 + module_init(wm8739_init_module); 355 + module_exit(wm8739_cleanup_module);