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/ioctl.h>
  31#include <asm/uaccess.h>
  32#include <linux/i2c.h>
  33#include <linux/i2c-id.h>
  34#include <linux/videodev2.h>
  35#include <media/v4l2-device.h>
  36#include <media/v4l2-chip-ident.h>
  37#include <media/v4l2-i2c-drv.h>
  38
  39MODULE_DESCRIPTION("wm8775 driver");
  40MODULE_AUTHOR("Ulf Eklund, Hans Verkuil");
  41MODULE_LICENSE("GPL");
  42
  43
  44
  45/* ----------------------------------------------------------------------- */
  46
  47enum {
  48        R7 = 7, R11 = 11,
  49        R12, R13, R14, R15, R16, R17, R18, R19, R20, R21, R23 = 23,
  50        TOT_REGS
  51};
  52
  53struct wm8775_state {
  54        struct v4l2_subdev sd;
  55        u8 input;               /* Last selected input (0-0xf) */
  56        u8 muted;
  57};
  58
  59static inline struct wm8775_state *to_state(struct v4l2_subdev *sd)
  60{
  61        return container_of(sd, struct wm8775_state, sd);
  62}
  63
  64static int wm8775_write(struct v4l2_subdev *sd, int reg, u16 val)
  65{
  66        struct i2c_client *client = v4l2_get_subdevdata(sd);
  67        int i;
  68
  69        if (reg < 0 || reg >= TOT_REGS) {
  70                v4l2_err(sd, "Invalid register R%d\n", reg);
  71                return -1;
  72        }
  73
  74        for (i = 0; i < 3; i++)
  75                if (i2c_smbus_write_byte_data(client,
  76                                (reg << 1) | (val >> 8), val & 0xff) == 0)
  77                        return 0;
  78        v4l2_err(sd, "I2C: cannot write %03x to register R%d\n", val, reg);
  79        return -1;
  80}
  81
  82static int wm8775_s_routing(struct v4l2_subdev *sd,
  83                            u32 input, u32 output, u32 config)
  84{
  85        struct wm8775_state *state = to_state(sd);
  86
  87        /* There are 4 inputs and one output. Zero or more inputs
  88           are multiplexed together to the output. Hence there are
  89           16 combinations.
  90           If only one input is active (the normal case) then the
  91           input values 1, 2, 4 or 8 should be used. */
  92        if (input > 15) {
  93                v4l2_err(sd, "Invalid input %d.\n", input);
  94                return -EINVAL;
  95        }
  96        state->input = input;
  97        if (state->muted)
  98                return 0;
  99        wm8775_write(sd, R21, 0x0c0);
 100        wm8775_write(sd, R14, 0x1d4);
 101        wm8775_write(sd, R15, 0x1d4);
 102        wm8775_write(sd, R21, 0x100 + state->input);
 103        return 0;
 104}
 105
 106static int wm8775_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
 107{
 108        struct wm8775_state *state = to_state(sd);
 109
 110        if (ctrl->id != V4L2_CID_AUDIO_MUTE)
 111                return -EINVAL;
 112        ctrl->value = state->muted;
 113        return 0;
 114}
 115
 116static int wm8775_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
 117{
 118        struct wm8775_state *state = to_state(sd);
 119
 120        if (ctrl->id != V4L2_CID_AUDIO_MUTE)
 121                return -EINVAL;
 122        state->muted = ctrl->value;
 123        wm8775_write(sd, R21, 0x0c0);
 124        wm8775_write(sd, R14, 0x1d4);
 125        wm8775_write(sd, R15, 0x1d4);
 126        if (!state->muted)
 127                wm8775_write(sd, R21, 0x100 + state->input);
 128        return 0;
 129}
 130
 131static int wm8775_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
 132{
 133        struct i2c_client *client = v4l2_get_subdevdata(sd);
 134
 135        return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_WM8775, 0);
 136}
 137
 138static int wm8775_log_status(struct v4l2_subdev *sd)
 139{
 140        struct wm8775_state *state = to_state(sd);
 141
 142        v4l2_info(sd, "Input: %d%s\n", state->input,
 143                        state->muted ? " (muted)" : "");
 144        return 0;
 145}
 146
 147static int wm8775_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq)
 148{
 149        struct wm8775_state *state = to_state(sd);
 150
 151        /* If I remove this, then it can happen that I have no
 152           sound the first time I tune from static to a valid channel.
 153           It's difficult to reproduce and is almost certainly related
 154           to the zero cross detect circuit. */
 155        wm8775_write(sd, R21, 0x0c0);
 156        wm8775_write(sd, R14, 0x1d4);
 157        wm8775_write(sd, R15, 0x1d4);
 158        wm8775_write(sd, R21, 0x100 + state->input);
 159        return 0;
 160}
 161
 162/* ----------------------------------------------------------------------- */
 163
 164static const struct v4l2_subdev_core_ops wm8775_core_ops = {
 165        .log_status = wm8775_log_status,
 166        .g_chip_ident = wm8775_g_chip_ident,
 167        .g_ctrl = wm8775_g_ctrl,
 168        .s_ctrl = wm8775_s_ctrl,
 169};
 170
 171static const struct v4l2_subdev_tuner_ops wm8775_tuner_ops = {
 172        .s_frequency = wm8775_s_frequency,
 173};
 174
 175static const struct v4l2_subdev_audio_ops wm8775_audio_ops = {
 176        .s_routing = wm8775_s_routing,
 177};
 178
 179static const struct v4l2_subdev_ops wm8775_ops = {
 180        .core = &wm8775_core_ops,
 181        .tuner = &wm8775_tuner_ops,
 182        .audio = &wm8775_audio_ops,
 183};
 184
 185/* ----------------------------------------------------------------------- */
 186
 187/* i2c implementation */
 188
 189/*
 190 * Generic i2c probe
 191 * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1'
 192 */
 193
 194static int wm8775_probe(struct i2c_client *client,
 195                        const struct i2c_device_id *id)
 196{
 197        struct wm8775_state *state;
 198        struct v4l2_subdev *sd;
 199
 200        /* Check if the adapter supports the needed features */
 201        if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
 202                return -EIO;
 203
 204        v4l_info(client, "chip found @ 0x%02x (%s)\n",
 205                        client->addr << 1, client->adapter->name);
 206
 207        state = kmalloc(sizeof(struct wm8775_state), GFP_KERNEL);
 208        if (state == NULL)
 209                return -ENOMEM;
 210        sd = &state->sd;
 211        v4l2_i2c_subdev_init(sd, client, &wm8775_ops);
 212        state->input = 2;
 213        state->muted = 0;
 214
 215        /* Initialize wm8775 */
 216
 217        /* RESET */
 218        wm8775_write(sd, R23, 0x000);
 219        /* Disable zero cross detect timeout */
 220        wm8775_write(sd, R7, 0x000);
 221        /* Left justified, 24-bit mode */
 222        wm8775_write(sd, R11, 0x021);
 223        /* Master mode, clock ratio 256fs */
 224        wm8775_write(sd, R12, 0x102);
 225        /* Powered up */
 226        wm8775_write(sd, R13, 0x000);
 227        /* ADC gain +2.5dB, enable zero cross */
 228        wm8775_write(sd, R14, 0x1d4);
 229        /* ADC gain +2.5dB, enable zero cross */
 230        wm8775_write(sd, R15, 0x1d4);
 231        /* ALC Stereo, ALC target level -1dB FS max gain +8dB */
 232        wm8775_write(sd, R16, 0x1bf);
 233        /* Enable gain control, use zero cross detection,
 234           ALC hold time 42.6 ms */
 235        wm8775_write(sd, R17, 0x185);
 236        /* ALC gain ramp up delay 34 s, ALC gain ramp down delay 33 ms */
 237        wm8775_write(sd, R18, 0x0a2);
 238        /* Enable noise gate, threshold -72dBfs */
 239        wm8775_write(sd, R19, 0x005);
 240        /* Transient window 4ms, lower PGA gain limit -1dB */
 241        wm8775_write(sd, R20, 0x07a);
 242        /* LRBOTH = 1, use input 2. */
 243        wm8775_write(sd, R21, 0x102);
 244        return 0;
 245}
 246
 247static int wm8775_remove(struct i2c_client *client)
 248{
 249        struct v4l2_subdev *sd = i2c_get_clientdata(client);
 250
 251        v4l2_device_unregister_subdev(sd);
 252        kfree(to_state(sd));
 253        return 0;
 254}
 255
 256static const struct i2c_device_id wm8775_id[] = {
 257        { "wm8775", 0 },
 258        { }
 259};
 260MODULE_DEVICE_TABLE(i2c, wm8775_id);
 261
 262static struct v4l2_i2c_driver_data v4l2_i2c_data = {
 263        .name = "wm8775",
 264        .probe = wm8775_probe,
 265        .remove = wm8775_remove,
 266        .id_table = wm8775_id,
 267};
 268