]> git.karo-electronics.de Git - mv-sheeva.git/blob - drivers/media/video/wm8775.c
V4L/DVB (3247): Replace AUDC_SET_INPUT with VIDIOC_S_AUDIO in wm8775.
[mv-sheeva.git] / drivers / media / video / wm8775.c
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/videodev.h>
35 #include <media/audiochip.h>
36
37 MODULE_DESCRIPTION("wm8775 driver");
38 MODULE_AUTHOR("Ulf Eklund, Hans Verkuil");
39 MODULE_LICENSE("GPL");
40
41 #define wm8775_err(fmt, arg...) do { \
42         printk(KERN_ERR "%s %d-%04x: " fmt, client->driver->driver.name, \
43                i2c_adapter_id(client->adapter), client->addr , ## arg); } while (0)
44 #define wm8775_info(fmt, arg...) do { \
45         printk(KERN_INFO "%s %d-%04x: " fmt, client->driver->driver.name, \
46                i2c_adapter_id(client->adapter), client->addr , ## arg); } while (0)
47
48
49 static unsigned short normal_i2c[] = { 0x36 >> 1, I2C_CLIENT_END };
50
51
52 I2C_CLIENT_INSMOD;
53
54 /* ----------------------------------------------------------------------- */
55
56 enum {
57         R7 = 7, R11 = 11,
58         R12, R13, R14, R15, R16, R17, R18, R19, R20, R21, R23 = 23,
59         TOT_REGS
60 };
61
62 struct wm8775_state {
63         u8 input;               /* Last selected input (0-0xf) */
64         u8 muted;
65 };
66
67 static int wm8775_write(struct i2c_client *client, int reg, u16 val)
68 {
69         int i;
70
71         if (reg < 0 || reg >= TOT_REGS) {
72                 wm8775_err("Invalid register R%d\n", reg);
73                 return -1;
74         }
75
76         for (i = 0; i < 3; i++) {
77                 if (i2c_smbus_write_byte_data(client, (reg << 1) |
78                                         (val >> 8), val & 0xff) == 0) {
79                         return 0;
80                 }
81         }
82         wm8775_err("I2C: cannot write %03x to register R%d\n", val, reg);
83         return -1;
84 }
85
86 static int wm8775_command(struct i2c_client *client, unsigned int cmd,
87                           void *arg)
88 {
89         struct wm8775_state *state = i2c_get_clientdata(client);
90         struct v4l2_audio *input = arg;
91         struct v4l2_control *ctrl = arg;
92
93         switch (cmd) {
94         case VIDIOC_S_AUDIO:
95                 /* There are 4 inputs and one output. Zero or more inputs
96                    are multiplexed together to the output. Hence there are
97                    16 combinations.
98                    If only one input is active (the normal case) then the
99                    input values 1, 2, 4 or 8 should be used. */
100                 if (input->index > 15) {
101                         wm8775_err("Invalid input %d.\n", input->index);
102                         return -EINVAL;
103                 }
104                 state->input = input->index;
105                 if (state->muted)
106                         break;
107                 wm8775_write(client, R21, 0x0c0);
108                 wm8775_write(client, R14, 0x1d4);
109                 wm8775_write(client, R15, 0x1d4);
110                 wm8775_write(client, R21, 0x100 + state->input);
111                 break;
112
113         case VIDIOC_G_AUDIO:
114                 memset(input, 0, sizeof(*input));
115                 input->index = state->input;
116                 break;
117
118         case VIDIOC_G_CTRL:
119                 if (ctrl->id != V4L2_CID_AUDIO_MUTE)
120                         return -EINVAL;
121                 ctrl->value = state->muted;
122                 break;
123
124         case VIDIOC_S_CTRL:
125                 if (ctrl->id != V4L2_CID_AUDIO_MUTE)
126                         return -EINVAL;
127                 state->muted = ctrl->value;
128                 wm8775_write(client, R21, 0x0c0);
129                 wm8775_write(client, R14, 0x1d4);
130                 wm8775_write(client, R15, 0x1d4);
131                 if (!state->muted)
132                         wm8775_write(client, R21, 0x100 + state->input);
133                 break;
134
135         case VIDIOC_LOG_STATUS:
136                 wm8775_info("Input: %d%s\n", state->input,
137                             state->muted ? " (muted)" : "");
138                 break;
139
140         case VIDIOC_S_FREQUENCY:
141                 /* If I remove this, then it can happen that I have no
142                    sound the first time I tune from static to a valid channel.
143                    It's difficult to reproduce and is almost certainly related
144                    to the zero cross detect circuit. */
145                 wm8775_write(client, R21, 0x0c0);
146                 wm8775_write(client, R14, 0x1d4);
147                 wm8775_write(client, R15, 0x1d4);
148                 wm8775_write(client, R21, 0x100 + state->input);
149                 break;
150
151         default:
152                 return -EINVAL;
153         }
154         return 0;
155 }
156
157 /* ----------------------------------------------------------------------- */
158
159 /* i2c implementation */
160
161 /*
162  * Generic i2c probe
163  * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1'
164  */
165
166 static struct i2c_driver i2c_driver;
167
168 static int wm8775_attach(struct i2c_adapter *adapter, int address, int kind)
169 {
170         struct i2c_client *client;
171         struct wm8775_state *state;
172
173         /* Check if the adapter supports the needed features */
174         if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
175                 return 0;
176
177         client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL);
178         if (client == 0)
179                 return -ENOMEM;
180
181         memset(client, 0, sizeof(struct i2c_client));
182         client->addr = address;
183         client->adapter = adapter;
184         client->driver = &i2c_driver;
185         snprintf(client->name, sizeof(client->name) - 1, "wm8775");
186
187         wm8775_info("chip found @ 0x%x (%s)\n", address << 1, adapter->name);
188
189         state = kmalloc(sizeof(struct wm8775_state), GFP_KERNEL);
190         if (state == NULL) {
191                 kfree(client);
192                 return -ENOMEM;
193         }
194         state->input = 2;
195         state->muted = 0;
196         i2c_set_clientdata(client, state);
197
198         /* initialize wm8775 */
199         wm8775_write(client, R23, 0x000);       /* RESET */
200         wm8775_write(client, R7, 0x000);        /* Disable zero cross detect timeout */
201         wm8775_write(client, R11, 0x021);       /* Left justified, 24-bit mode */
202         wm8775_write(client, R12, 0x102);       /* Master mode, clock ratio 256fs */
203         wm8775_write(client, R13, 0x000);       /* Powered up */
204         wm8775_write(client, R14, 0x1d4);       /* ADC gain +2.5dB, enable zero cross */
205         wm8775_write(client, R15, 0x1d4);       /* ADC gain +2.5dB, enable zero cross */
206         wm8775_write(client, R16, 0x1bf);       /* ALC Stereo, ALC target level -1dB FS */
207         /* max gain +8dB */
208         wm8775_write(client, R17, 0x185);       /* Enable gain control, use zero cross */
209         /* detection, ALC hold time 42.6 ms */
210         wm8775_write(client, R18, 0x0a2);       /* ALC gain ramp up delay 34 s, */
211         /* ALC gain ramp down delay 33 ms */
212         wm8775_write(client, R19, 0x005);       /* Enable noise gate, threshold -72dBfs */
213         wm8775_write(client, R20, 0x07a);       /* Transient window 4ms, lower PGA gain */
214         /* limit -1dB */
215         wm8775_write(client, R21, 0x102);       /* LRBOTH = 1, use input 2. */
216         i2c_attach_client(client);
217
218         return 0;
219 }
220
221 static int wm8775_probe(struct i2c_adapter *adapter)
222 {
223         if (adapter->class & I2C_CLASS_TV_ANALOG)
224                 return i2c_probe(adapter, &addr_data, wm8775_attach);
225         return 0;
226 }
227
228 static int wm8775_detach(struct i2c_client *client)
229 {
230         int err;
231
232         err = i2c_detach_client(client);
233         if (err) {
234                 return err;
235         }
236         kfree(client);
237
238         return 0;
239 }
240
241 /* ----------------------------------------------------------------------- */
242
243 /* i2c implementation */
244 static struct i2c_driver i2c_driver = {
245         .driver = {
246                 .name = "wm8775",
247         },
248
249         .id = I2C_DRIVERID_WM8775,
250
251         .attach_adapter = wm8775_probe,
252         .detach_client = wm8775_detach,
253         .command = wm8775_command,
254 };
255
256
257 static int __init wm8775_init_module(void)
258 {
259         return i2c_add_driver(&i2c_driver);
260 }
261
262 static void __exit wm8775_cleanup_module(void)
263 {
264         i2c_del_driver(&i2c_driver);
265 }
266
267 module_init(wm8775_init_module);
268 module_exit(wm8775_cleanup_module);