]>
Commit | Line | Data |
---|---|---|
75c4570c HV |
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> | |
74cab31c | 32 | #include <media/v4l2-chip-ident.h> |
f69d419a | 33 | #include <media/v4l2-i2c-drv.h> |
75c4570c HV |
34 | |
35 | MODULE_DESCRIPTION("wm8739 driver"); | |
36 | MODULE_AUTHOR("T. Adachi, Hans Verkuil"); | |
37 | MODULE_LICENSE("GPL"); | |
38 | ||
099b8e73 | 39 | static int debug; |
75c4570c HV |
40 | |
41 | module_param(debug, int, 0644); | |
42 | ||
43 | MODULE_PARM_DESC(debug, "Debug level (0-1)"); | |
44 | ||
45 | ||
75c4570c HV |
46 | /* ------------------------------------------------------------------------ */ |
47 | ||
48 | enum { | |
49 | R0 = 0, R1, | |
50 | R5 = 5, R6, R7, R8, R9, R15 = 15, | |
51 | TOT_REGS | |
52 | }; | |
53 | ||
54 | struct wm8739_state { | |
55 | u32 clock_freq; | |
56 | u8 muted; | |
57 | u16 volume; | |
58 | u16 balance; | |
59 | u8 vol_l; /* +12dB to -34.5dB 1.5dB step (5bit) def:0dB */ | |
60 | u8 vol_r; /* +12dB to -34.5dB 1.5dB step (5bit) def:0dB */ | |
61 | }; | |
62 | ||
63 | /* ------------------------------------------------------------------------ */ | |
64 | ||
65 | static int wm8739_write(struct i2c_client *client, int reg, u16 val) | |
66 | { | |
67 | int i; | |
68 | ||
69 | if (reg < 0 || reg >= TOT_REGS) { | |
70 | v4l_err(client, "Invalid register R%d\n", reg); | |
71 | return -1; | |
72 | } | |
73 | ||
74 | v4l_dbg(1, debug, client, "write: %02x %02x\n", reg, val); | |
75 | ||
099b8e73 HV |
76 | for (i = 0; i < 3; i++) |
77 | if (i2c_smbus_write_byte_data(client, | |
78 | (reg << 1) | (val >> 8), val & 0xff) == 0) | |
75c4570c | 79 | return 0; |
75c4570c HV |
80 | v4l_err(client, "I2C: cannot write %03x to register R%d\n", val, reg); |
81 | return -1; | |
82 | } | |
83 | ||
84 | /* write regs to set audio volume etc */ | |
85 | static void wm8739_set_audio(struct i2c_client *client) | |
86 | { | |
87 | struct wm8739_state *state = i2c_get_clientdata(client); | |
88 | u16 mute = state->muted ? 0x80 : 0; | |
89 | ||
90 | /* Volume setting: bits 0-4, 0x1f = 12 dB, 0x00 = -34.5 dB | |
91 | * Default setting: 0x17 = 0 dB | |
92 | */ | |
93 | wm8739_write(client, R0, (state->vol_l & 0x1f) | mute); | |
94 | wm8739_write(client, R1, (state->vol_r & 0x1f) | mute); | |
95 | } | |
96 | ||
97 | static int wm8739_get_ctrl(struct i2c_client *client, struct v4l2_control *ctrl) | |
98 | { | |
99 | struct wm8739_state *state = i2c_get_clientdata(client); | |
100 | ||
101 | switch (ctrl->id) { | |
102 | case V4L2_CID_AUDIO_MUTE: | |
103 | ctrl->value = state->muted; | |
104 | break; | |
105 | ||
106 | case V4L2_CID_AUDIO_VOLUME: | |
107 | ctrl->value = state->volume; | |
108 | break; | |
109 | ||
110 | case V4L2_CID_AUDIO_BALANCE: | |
111 | ctrl->value = state->balance; | |
112 | break; | |
113 | ||
114 | default: | |
115 | return -EINVAL; | |
116 | } | |
117 | return 0; | |
118 | } | |
119 | ||
120 | static int wm8739_set_ctrl(struct i2c_client *client, struct v4l2_control *ctrl) | |
121 | { | |
122 | struct wm8739_state *state = i2c_get_clientdata(client); | |
123 | unsigned int work_l, work_r; | |
124 | ||
125 | switch (ctrl->id) { | |
126 | case V4L2_CID_AUDIO_MUTE: | |
127 | state->muted = ctrl->value; | |
128 | break; | |
129 | ||
130 | case V4L2_CID_AUDIO_VOLUME: | |
131 | state->volume = ctrl->value; | |
132 | break; | |
133 | ||
134 | case V4L2_CID_AUDIO_BALANCE: | |
135 | state->balance = ctrl->value; | |
136 | break; | |
137 | ||
138 | default: | |
139 | return -EINVAL; | |
140 | } | |
141 | ||
142 | /* normalize ( 65535 to 0 -> 31 to 0 (12dB to -34.5dB) ) */ | |
143 | work_l = (min(65536 - state->balance, 32768) * state->volume) / 32768; | |
144 | work_r = (min(state->balance, (u16)32768) * state->volume) / 32768; | |
145 | ||
146 | state->vol_l = (long)work_l * 31 / 65535; | |
147 | state->vol_r = (long)work_r * 31 / 65535; | |
148 | ||
149 | /* set audio volume etc. */ | |
150 | wm8739_set_audio(client); | |
151 | return 0; | |
152 | } | |
153 | ||
154 | /* ------------------------------------------------------------------------ */ | |
155 | ||
156 | static struct v4l2_queryctrl wm8739_qctrl[] = { | |
157 | { | |
158 | .id = V4L2_CID_AUDIO_VOLUME, | |
159 | .name = "Volume", | |
160 | .minimum = 0, | |
161 | .maximum = 65535, | |
162 | .step = 65535/100, | |
163 | .default_value = 58880, | |
164 | .flags = 0, | |
165 | .type = V4L2_CTRL_TYPE_INTEGER, | |
099b8e73 | 166 | }, { |
75c4570c HV |
167 | .id = V4L2_CID_AUDIO_MUTE, |
168 | .name = "Mute", | |
169 | .minimum = 0, | |
170 | .maximum = 1, | |
171 | .step = 1, | |
172 | .default_value = 1, | |
173 | .flags = 0, | |
174 | .type = V4L2_CTRL_TYPE_BOOLEAN, | |
099b8e73 | 175 | }, { |
75c4570c HV |
176 | .id = V4L2_CID_AUDIO_BALANCE, |
177 | .name = "Balance", | |
178 | .minimum = 0, | |
179 | .maximum = 65535, | |
180 | .step = 65535/100, | |
181 | .default_value = 32768, | |
182 | .flags = 0, | |
183 | .type = V4L2_CTRL_TYPE_INTEGER, | |
184 | } | |
185 | }; | |
186 | ||
187 | /* ------------------------------------------------------------------------ */ | |
188 | ||
099b8e73 | 189 | static int wm8739_command(struct i2c_client *client, unsigned cmd, void *arg) |
75c4570c HV |
190 | { |
191 | struct wm8739_state *state = i2c_get_clientdata(client); | |
192 | ||
193 | switch (cmd) { | |
194 | case VIDIOC_INT_AUDIO_CLOCK_FREQ: | |
195 | { | |
196 | u32 audiofreq = *(u32 *)arg; | |
197 | ||
198 | state->clock_freq = audiofreq; | |
099b8e73 HV |
199 | /* de-activate */ |
200 | wm8739_write(client, R9, 0x000); | |
75c4570c HV |
201 | switch (audiofreq) { |
202 | case 44100: | |
099b8e73 HV |
203 | /* 256fps, fs=44.1k */ |
204 | wm8739_write(client, R8, 0x020); | |
75c4570c HV |
205 | break; |
206 | case 48000: | |
099b8e73 HV |
207 | /* 256fps, fs=48k */ |
208 | wm8739_write(client, R8, 0x000); | |
75c4570c HV |
209 | break; |
210 | case 32000: | |
099b8e73 HV |
211 | /* 256fps, fs=32k */ |
212 | wm8739_write(client, R8, 0x018); | |
75c4570c HV |
213 | break; |
214 | default: | |
215 | break; | |
216 | } | |
099b8e73 HV |
217 | /* activate */ |
218 | wm8739_write(client, R9, 0x001); | |
75c4570c HV |
219 | break; |
220 | } | |
221 | ||
222 | case VIDIOC_G_CTRL: | |
223 | return wm8739_get_ctrl(client, arg); | |
224 | ||
225 | case VIDIOC_S_CTRL: | |
226 | return wm8739_set_ctrl(client, arg); | |
227 | ||
228 | case VIDIOC_QUERYCTRL: | |
229 | { | |
230 | struct v4l2_queryctrl *qc = arg; | |
231 | int i; | |
232 | ||
233 | for (i = 0; i < ARRAY_SIZE(wm8739_qctrl); i++) | |
234 | if (qc->id && qc->id == wm8739_qctrl[i].id) { | |
235 | memcpy(qc, &wm8739_qctrl[i], sizeof(*qc)); | |
236 | return 0; | |
237 | } | |
238 | return -EINVAL; | |
239 | } | |
240 | ||
74cab31c | 241 | case VIDIOC_G_CHIP_IDENT: |
099b8e73 HV |
242 | return v4l2_chip_ident_i2c_client(client, |
243 | arg, V4L2_IDENT_WM8739, 0); | |
74cab31c | 244 | |
75c4570c HV |
245 | case VIDIOC_LOG_STATUS: |
246 | v4l_info(client, "Frequency: %u Hz\n", state->clock_freq); | |
247 | v4l_info(client, "Volume L: %02x%s\n", state->vol_l & 0x1f, | |
248 | state->muted ? " (muted)" : ""); | |
249 | v4l_info(client, "Volume R: %02x%s\n", state->vol_r & 0x1f, | |
250 | state->muted ? " (muted)" : ""); | |
251 | break; | |
252 | ||
253 | default: | |
254 | return -EINVAL; | |
255 | } | |
256 | ||
257 | return 0; | |
258 | } | |
259 | ||
260 | /* ------------------------------------------------------------------------ */ | |
261 | ||
262 | /* i2c implementation */ | |
263 | ||
d2653e92 JD |
264 | static int wm8739_probe(struct i2c_client *client, |
265 | const struct i2c_device_id *id) | |
75c4570c | 266 | { |
75c4570c HV |
267 | struct wm8739_state *state; |
268 | ||
188f3457 HV |
269 | /* Check if the adapter supports the needed features */ |
270 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) | |
271 | return -EIO; | |
272 | ||
099b8e73 HV |
273 | v4l_info(client, "chip found @ 0x%x (%s)\n", |
274 | client->addr << 1, client->adapter->name); | |
75c4570c HV |
275 | |
276 | state = kmalloc(sizeof(struct wm8739_state), GFP_KERNEL); | |
277 | if (state == NULL) { | |
278 | kfree(client); | |
279 | return -ENOMEM; | |
280 | } | |
281 | state->vol_l = 0x17; /* 0dB */ | |
282 | state->vol_r = 0x17; /* 0dB */ | |
283 | state->muted = 0; | |
284 | state->balance = 32768; | |
285 | /* normalize (12dB(31) to -34.5dB(0) [0dB(23)] -> 65535 to 0) */ | |
286 | state->volume = ((long)state->vol_l + 1) * 65535 / 31; | |
287 | state->clock_freq = 48000; | |
288 | i2c_set_clientdata(client, state); | |
289 | ||
099b8e73 HV |
290 | /* Initialize wm8739 */ |
291 | ||
292 | /* reset */ | |
293 | wm8739_write(client, R15, 0x00); | |
294 | /* filter setting, high path, offet clear */ | |
295 | wm8739_write(client, R5, 0x000); | |
296 | /* ADC, OSC, Power Off mode Disable */ | |
297 | wm8739_write(client, R6, 0x000); | |
298 | /* Digital Audio interface format: | |
299 | Enable Master mode, 24 bit, MSB first/left justified */ | |
300 | wm8739_write(client, R7, 0x049); | |
301 | /* sampling control: normal, 256fs, 48KHz sampling rate */ | |
302 | wm8739_write(client, R8, 0x000); | |
303 | /* activate */ | |
304 | wm8739_write(client, R9, 0x001); | |
305 | /* set volume/mute */ | |
306 | wm8739_set_audio(client); | |
75c4570c HV |
307 | return 0; |
308 | } | |
309 | ||
49457cc2 | 310 | static int wm8739_remove(struct i2c_client *client) |
75c4570c | 311 | { |
49457cc2 | 312 | kfree(i2c_get_clientdata(client)); |
75c4570c HV |
313 | return 0; |
314 | } | |
315 | ||
af294867 JD |
316 | static const struct i2c_device_id wm8739_id[] = { |
317 | { "wm8739", 0 }, | |
318 | { } | |
319 | }; | |
320 | MODULE_DEVICE_TABLE(i2c, wm8739_id); | |
321 | ||
49457cc2 HV |
322 | static struct v4l2_i2c_driver_data v4l2_i2c_data = { |
323 | .name = "wm8739", | |
324 | .driverid = I2C_DRIVERID_WM8739, | |
75c4570c | 325 | .command = wm8739_command, |
49457cc2 HV |
326 | .probe = wm8739_probe, |
327 | .remove = wm8739_remove, | |
af294867 | 328 | .id_table = wm8739_id, |
75c4570c | 329 | }; |