linux/drivers/media/video/wm8775.c
<<
>>
Prefs
   1/*
   2 * wm8775 - driver version 0.0.1
   3 *
   4 * Copyright (C) 2004 Ulf Eklund <ivtv at eklund.to>
   5 *
   6 * Based on saa7115 driver
   7 *
   8 * Copyright (C) 2005 Hans Verkuil <hverkuil@xs4all.nl>
   9 * - Cleanup
  10 * - V4L2 API update
  11 * - sound fixes
  12 *
  13 * This program is free software; you can redistribute it and/or modify
  14 * it under the terms of the GNU General Public License as published by
  15 * the Free Software Foundation; either version 2 of the License, or
  16 * (at your option) any later version.
  17 *
  18 * This program is distributed in the hope that it will be useful,
  19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  21 * GNU General Public License for more details.
  22 *
  23 * You should have received a copy of the GNU General Public License
  24 * along with this program; if not, write to the Free Software
  25 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  26 */
  27
  28#include <linux/module.h>
  29#include <linux/types.h>
  30#include <linux/slab.h>
  31#include <linux/ioctl.h>
  32#include <asm/uaccess.h>
  33#include <linux/i2c.h>
  34#include <linux/videodev2.h>
  35#include <media/v4l2-device.h>
  36#include <media/v4l2-chip-ident.h>
  37#include <media/v4l2-ctrls.h>
  38#include <media/wm8775.h>
  39
  40MODULE_DESCRIPTION("wm8775 driver");
  41MODULE_AUTHOR("Ulf Eklund, Hans Verkuil");
  42MODULE_LICENSE("GPL");
  43
  44
  45
  46/* ----------------------------------------------------------------------- */
  47
  48enum {
  49        R7 = 7, R11 = 11,
  50        R12, R13, R14, R15, R16, R17, R18, R19, R20, R21, R23 = 23,
  51        TOT_REGS
  52};
  53
  54#define ALC_HOLD 0x85 /* R17: use zero cross detection, ALC hold time 42.6 ms */
  55#define ALC_EN 0x100  /* R17: ALC enable */
  56
  57struct wm8775_state {
  58        struct v4l2_subdev sd;
  59        struct v4l2_ctrl_handler hdl;
  60        struct v4l2_ctrl *mute;
  61        struct v4l2_ctrl *vol;
  62        struct v4l2_ctrl *bal;
  63        struct v4l2_ctrl *loud;
  64        u8 input;               /* Last selected input (0-0xf) */
  65};
  66
  67static inline struct wm8775_state *to_state(struct v4l2_subdev *sd)
  68{
  69        return container_of(sd, struct wm8775_state, sd);
  70}
  71
  72static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
  73{
  74        return &container_of(ctrl->handler, struct wm8775_state, hdl)->sd;
  75}
  76
  77static int wm8775_write(struct v4l2_subdev *sd, int reg, u16 val)
  78{
  79        struct i2c_client *client = v4l2_get_subdevdata(sd);
  80        int i;
  81
  82        if (reg < 0 || reg >= TOT_REGS) {
  83                v4l2_err(sd, "Invalid register R%d\n", reg);
  84                return -1;
  85        }
  86
  87        for (i = 0; i < 3; i++)
  88                if (i2c_smbus_write_byte_data(client,
  89                                (reg << 1) | (val >> 8), val & 0xff) == 0)
  90                        return 0;
  91        v4l2_err(sd, "I2C: cannot write %03x to register R%d\n", val, reg);
  92        return -1;
  93}
  94
  95static void wm8775_set_audio(struct v4l2_subdev *sd, int quietly)
  96{
  97        struct wm8775_state *state = to_state(sd);
  98        u8 vol_l, vol_r;
  99        int muted = 0 != state->mute->val;
 100        u16 volume = (u16)state->vol->val;
 101        u16 balance = (u16)state->bal->val;
 102
 103        /* normalize ( 65535 to 0 -> 255 to 0 (+24dB to -103dB) ) */
 104        vol_l = (min(65536 - balance, 32768) * volume) >> 23;
 105        vol_r = (min(balance, (u16)32768) * volume) >> 23;
 106
 107        /* Mute */
 108        if (muted || quietly)
 109                wm8775_write(sd, R21, 0x0c0 | state->input);
 110
 111        wm8775_write(sd, R14, vol_l | 0x100); /* 0x100= Left channel ADC zero cross enable */
 112        wm8775_write(sd, R15, vol_r | 0x100); /* 0x100= Right channel ADC zero cross enable */
 113
 114        /* Un-mute */
 115        if (!muted)
 116                wm8775_write(sd, R21, state->input);
 117}
 118
 119static int wm8775_s_routing(struct v4l2_subdev *sd,
 120                            u32 input, u32 output, u32 config)
 121{
 122        struct wm8775_state *state = to_state(sd);
 123
 124        /* There are 4 inputs and one output. Zero or more inputs
 125           are multiplexed together to the output. Hence there are
 126           16 combinations.
 127           If only one input is active (the normal case) then the
 128           input values 1, 2, 4 or 8 should be used. */
 129        if (input > 15) {
 130                v4l2_err(sd, "Invalid input %d.\n", input);
 131                return -EINVAL;
 132        }
 133        state->input = input;
 134        if (!v4l2_ctrl_g_ctrl(state->mute))
 135                return 0;
 136        if (!v4l2_ctrl_g_ctrl(state->vol))
 137                return 0;
 138        if (!v4l2_ctrl_g_ctrl(state->bal))
 139                return 0;
 140        wm8775_set_audio(sd, 1);
 141        return 0;
 142}
 143
 144static int wm8775_s_ctrl(struct v4l2_ctrl *ctrl)
 145{
 146        struct v4l2_subdev *sd = to_sd(ctrl);
 147
 148        switch (ctrl->id) {
 149        case V4L2_CID_AUDIO_MUTE:
 150        case V4L2_CID_AUDIO_VOLUME:
 151        case V4L2_CID_AUDIO_BALANCE:
 152                wm8775_set_audio(sd, 0);
 153                return 0;
 154        case V4L2_CID_AUDIO_LOUDNESS:
 155                wm8775_write(sd, R17, (ctrl->val ? ALC_EN : 0) | ALC_HOLD);
 156                return 0;
 157        }
 158        return -EINVAL;
 159}
 160
 161static int wm8775_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
 162{
 163        struct i2c_client *client = v4l2_get_subdevdata(sd);
 164
 165        return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_WM8775, 0);
 166}
 167
 168static int wm8775_log_status(struct v4l2_subdev *sd)
 169{
 170        struct wm8775_state *state = to_state(sd);
 171
 172        v4l2_info(sd, "Input: %d\n", state->input);
 173        v4l2_ctrl_handler_log_status(&state->hdl, sd->name);
 174        return 0;
 175}
 176
 177static int wm8775_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq)
 178{
 179        wm8775_set_audio(sd, 0);
 180        return 0;
 181}
 182
 183/* ----------------------------------------------------------------------- */
 184
 185static const struct v4l2_ctrl_ops wm8775_ctrl_ops = {
 186        .s_ctrl = wm8775_s_ctrl,
 187};
 188
 189static const struct v4l2_subdev_core_ops wm8775_core_ops = {
 190        .log_status = wm8775_log_status,
 191        .g_chip_ident = wm8775_g_chip_ident,
 192        .g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
 193        .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
 194        .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
 195        .g_ctrl = v4l2_subdev_g_ctrl,
 196        .s_ctrl = v4l2_subdev_s_ctrl,
 197        .queryctrl = v4l2_subdev_queryctrl,
 198        .querymenu = v4l2_subdev_querymenu,
 199};
 200
 201static const struct v4l2_subdev_tuner_ops wm8775_tuner_ops = {
 202        .s_frequency = wm8775_s_frequency,
 203};
 204
 205static const struct v4l2_subdev_audio_ops wm8775_audio_ops = {
 206        .s_routing = wm8775_s_routing,
 207};
 208
 209static const struct v4l2_subdev_ops wm8775_ops = {
 210        .core = &wm8775_core_ops,
 211        .tuner = &wm8775_tuner_ops,
 212        .audio = &wm8775_audio_ops,
 213};
 214
 215/* ----------------------------------------------------------------------- */
 216
 217/* i2c implementation */
 218
 219/*
 220 * Generic i2c probe
 221 * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1'
 222 */
 223
 224static int wm8775_probe(struct i2c_client *client,
 225                        const struct i2c_device_id *id)
 226{
 227        struct wm8775_state *state;
 228        struct v4l2_subdev *sd;
 229        int err;
 230        bool is_nova_s = false;
 231
 232        if (client->dev.platform_data) {
 233                struct wm8775_platform_data *data = client->dev.platform_data;
 234                is_nova_s = data->is_nova_s;
 235        }
 236
 237        /* Check if the adapter supports the needed features */
 238        if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
 239                return -EIO;
 240
 241        v4l_info(client, "chip found @ 0x%02x (%s)\n",
 242                        client->addr << 1, client->adapter->name);
 243
 244        state = kzalloc(sizeof(struct wm8775_state), GFP_KERNEL);
 245        if (state == NULL)
 246                return -ENOMEM;
 247        sd = &state->sd;
 248        v4l2_i2c_subdev_init(sd, client, &wm8775_ops);
 249        state->input = 2;
 250
 251        v4l2_ctrl_handler_init(&state->hdl, 4);
 252        state->mute = v4l2_ctrl_new_std(&state->hdl, &wm8775_ctrl_ops,
 253                        V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0);
 254        state->vol = v4l2_ctrl_new_std(&state->hdl, &wm8775_ctrl_ops,
 255                        V4L2_CID_AUDIO_VOLUME, 0, 65535, (65535+99)/100, 0xCF00); /* 0dB*/
 256        state->bal = v4l2_ctrl_new_std(&state->hdl, &wm8775_ctrl_ops,
 257                        V4L2_CID_AUDIO_BALANCE, 0, 65535, (65535+99)/100, 32768);
 258        state->loud = v4l2_ctrl_new_std(&state->hdl, &wm8775_ctrl_ops,
 259                        V4L2_CID_AUDIO_LOUDNESS, 0, 1, 1, 1);
 260        sd->ctrl_handler = &state->hdl;
 261        err = state->hdl.error;
 262        if (err) {
 263                v4l2_ctrl_handler_free(&state->hdl);
 264                kfree(state);
 265                return err;
 266        }
 267
 268        /* Initialize wm8775 */
 269
 270        /* RESET */
 271        wm8775_write(sd, R23, 0x000);
 272        /* Disable zero cross detect timeout */
 273        wm8775_write(sd, R7, 0x000);
 274        /* HPF enable, left justified, 24-bit (Philips) mode */
 275        wm8775_write(sd, R11, 0x021);
 276        /* Master mode, clock ratio 256fs */
 277        wm8775_write(sd, R12, 0x102);
 278        /* Powered up */
 279        wm8775_write(sd, R13, 0x000);
 280
 281        if (!is_nova_s) {
 282                /* ADC gain +2.5dB, enable zero cross */
 283                wm8775_write(sd, R14, 0x1d4);
 284                /* ADC gain +2.5dB, enable zero cross */
 285                wm8775_write(sd, R15, 0x1d4);
 286                /* ALC Stereo, ALC target level -1dB FS max gain +8dB */
 287                wm8775_write(sd, R16, 0x1bf);
 288                /* Enable gain control, use zero cross detection,
 289                   ALC hold time 42.6 ms */
 290                wm8775_write(sd, R17, 0x185);
 291        } else {
 292                /* ALC stereo, ALC target level -5dB FS, ALC max gain +8dB */
 293                wm8775_write(sd, R16, 0x1bb);
 294                /* Set ALC mode and hold time */
 295                wm8775_write(sd, R17, (state->loud->val ? ALC_EN : 0) | ALC_HOLD);
 296        }
 297        /* ALC gain ramp up delay 34 s, ALC gain ramp down delay 33 ms */
 298        wm8775_write(sd, R18, 0x0a2);
 299        /* Enable noise gate, threshold -72dBfs */
 300        wm8775_write(sd, R19, 0x005);
 301        if (!is_nova_s) {
 302                /* Transient window 4ms, lower PGA gain limit -1dB */
 303                wm8775_write(sd, R20, 0x07a);
 304                /* LRBOTH = 1, use input 2. */
 305                wm8775_write(sd, R21, 0x102);
 306        } else {
 307                /* Transient window 4ms, ALC min gain -5dB  */
 308                wm8775_write(sd, R20, 0x0fb);
 309
 310                wm8775_set_audio(sd, 1);      /* set volume/mute/mux */
 311        }
 312        return 0;
 313}
 314
 315static int wm8775_remove(struct i2c_client *client)
 316{
 317        struct v4l2_subdev *sd = i2c_get_clientdata(client);
 318        struct wm8775_state *state = to_state(sd);
 319
 320        v4l2_device_unregister_subdev(sd);
 321        v4l2_ctrl_handler_free(&state->hdl);
 322        kfree(state);
 323        return 0;
 324}
 325
 326static const struct i2c_device_id wm8775_id[] = {
 327        { "wm8775", 0 },
 328        { }
 329};
 330MODULE_DEVICE_TABLE(i2c, wm8775_id);
 331
 332static struct i2c_driver wm8775_driver = {
 333        .driver = {
 334                .owner  = THIS_MODULE,
 335                .name   = "wm8775",
 336        },
 337        .probe          = wm8775_probe,
 338        .remove         = wm8775_remove,
 339        .id_table       = wm8775_id,
 340};
 341
 342module_i2c_driver(wm8775_driver);
 343