Root/
Source at commit 396052adc2acd662edcf725e0be206fb16060333 created 11 years 6 months ago. By Marcos Paulo de Souza, media/radio/radio-rda5807: Use module_i2c_driver instead of module_{init|exit} | |
---|---|
1 | /* |
2 | * radio-rda5807.c - Driver for using the RDA5807 FM tuner chip via I2C |
3 | * |
4 | * Copyright (c) 2011 Maarten ter Huurne <maarten@treewalker.org> |
5 | * |
6 | * This program is free software; you can redistribute it and/or modify |
7 | * it under the terms of the GNU General Public License version 2 as |
8 | * published by the Free Software Foundation. |
9 | * |
10 | * This program is distributed in the hope that it will be useful, |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13 | * GNU General Public License for more details. |
14 | * |
15 | * You should have received a copy of the GNU General Public License |
16 | * along with this program; if not, write to the Free Software |
17 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
18 | * |
19 | * |
20 | * Many thanks to Jérôme VERES for his command line radio application that |
21 | * demonstrates how the chip can be controlled via I2C. |
22 | * |
23 | * The RDA5807 has three ways of accessing registers: |
24 | * - I2C address 0x10: sequential access, RDA5800 style |
25 | * - I2C address 0x11: random access |
26 | * - I2C address 0x60: sequential access, TEA5767 compatible |
27 | * This driver uses random access and therefore the i2c_board_info should |
28 | * specify address 0x11 using the macro RDA5807_I2C_ADDR. |
29 | * Note that while there are many similarities, the register map of the RDA5807 |
30 | * differs from that of the RDA5800 in several essential places. |
31 | */ |
32 | |
33 | |
34 | #include <asm/byteorder.h> |
35 | #include <linux/bitops.h> |
36 | #include <linux/delay.h> |
37 | #include <linux/module.h> |
38 | #include <linux/i2c.h> |
39 | #include <linux/kernel.h> |
40 | #include <linux/pm.h> |
41 | #include <linux/slab.h> |
42 | #include <linux/types.h> |
43 | #include <linux/videodev2.h> |
44 | #include <media/radio-rda5807.h> |
45 | #include <media/v4l2-ctrls.h> |
46 | #include <media/v4l2-dev.h> |
47 | #include <media/v4l2-ioctl.h> |
48 | |
49 | |
50 | enum rda5807_reg { |
51 | RDA5807_REG_CHIPID = 0x00, |
52 | RDA5807_REG_CTRL = 0x02, |
53 | RDA5807_REG_CHAN = 0x03, |
54 | RDA5807_REG_IOCFG = 0x04, |
55 | RDA5807_REG_INTM_THRESH_VOL = 0x05, |
56 | RDA5807_REG_SEEK_RESULT = 0x0A, |
57 | RDA5807_REG_SIGNAL = 0x0B, |
58 | }; |
59 | |
60 | #define RDA5807_MASK_CTRL_DHIZ BIT(15) |
61 | #define RDA5807_MASK_CTRL_DMUTE BIT(14) |
62 | #define RDA5807_MASK_CTRL_MONO BIT(13) |
63 | #define RDA5807_MASK_CTRL_BASS BIT(12) |
64 | #define RDA5807_MASK_CTRL_SEEKUP BIT(9) |
65 | #define RDA5807_MASK_CTRL_SEEK BIT(8) |
66 | #define RDA5807_MASK_CTRL_SKMODE BIT(7) |
67 | #define RDA5807_MASK_CTRL_CLKMODE (7 << 4) |
68 | #define RDA5807_MASK_CTRL_SOFTRESET BIT(1) |
69 | #define RDA5807_MASK_CTRL_ENABLE BIT(0) |
70 | |
71 | #define RDA5807_SHIFT_CHAN_WRCHAN 6 |
72 | #define RDA5807_MASK_CHAN_WRCHAN (0x3FF << RDA5807_SHIFT_CHAN_WRCHAN) |
73 | #define RDA5807_MASK_CHAN_TUNE BIT(4) |
74 | #define RDA5807_SHIFT_CHAN_BAND 2 |
75 | #define RDA5807_MASK_CHAN_BAND (0x3 << RDA5807_SHIFT_CHAN_BAND) |
76 | #define RDA5807_SHIFT_CHAN_SPACE 0 |
77 | #define RDA5807_MASK_CHAN_SPACE (0x3 << RDA5807_SHIFT_CHAN_SPACE) |
78 | |
79 | #define RDA5807_MASK_SEEKRES_COMPLETE BIT(14) |
80 | #define RDA5807_MASK_SEEKRES_FAIL BIT(13) |
81 | #define RDA5807_MASK_SEEKRES_STEREO BIT(10) |
82 | #define RDA5807_MASK_SEEKRES_READCHAN 0x3FF |
83 | |
84 | #define RDA5807_MASK_DEEMPHASIS BIT(11) |
85 | |
86 | #define RDA5807_SHIFT_VOLUME_DAC 0 |
87 | #define RDA5807_MASK_VOLUME_DAC (0xF << RDA5807_SHIFT_VOLUME_DAC) |
88 | |
89 | #define RDA5807_SHIFT_RSSI 9 |
90 | #define RDA5807_MASK_RSSI (0x7F << RDA5807_SHIFT_RSSI) |
91 | |
92 | #define RDA5807_FREQ_MIN_KHZ 76000 |
93 | #define RDA5807_FREQ_MAX_KHZ 108000 |
94 | |
95 | static int rda5807_i2c_read(struct i2c_client *client, enum rda5807_reg reg) |
96 | { |
97 | __u8 reg_buf = reg; |
98 | __u16 val_buf; |
99 | struct i2c_msg msgs[] = { |
100 | { /* write register number */ |
101 | .addr = client->addr, |
102 | .flags = 0, |
103 | .len = sizeof(reg_buf), |
104 | .buf = ®_buf, |
105 | }, |
106 | { /* read register contents */ |
107 | .addr = client->addr, |
108 | .flags = I2C_M_RD, |
109 | .len = sizeof(val_buf), |
110 | .buf = (__u8 *)&val_buf, |
111 | }, |
112 | }; |
113 | int err; |
114 | |
115 | err = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); |
116 | if (err < 0) return err; |
117 | if (err < ARRAY_SIZE(msgs)) return -EIO; |
118 | |
119 | dev_info(&client->dev, "reg[%02X] = %04X\n", reg, be16_to_cpu(val_buf)); |
120 | return be16_to_cpu(val_buf); |
121 | } |
122 | |
123 | static int rda5807_i2c_write(struct i2c_client *client, enum rda5807_reg reg, |
124 | u16 val) |
125 | { |
126 | __u8 buf[] = { reg, val >> 8, val & 0xFF }; |
127 | struct i2c_msg msgs[] = { |
128 | { /* write register number and contents */ |
129 | .addr = client->addr, |
130 | .flags = 0, |
131 | .len = sizeof(buf), |
132 | .buf = buf, |
133 | }, |
134 | }; |
135 | int err; |
136 | |
137 | err = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); |
138 | if (err < 0) return err; |
139 | if (err < ARRAY_SIZE(msgs)) return -EIO; |
140 | |
141 | dev_info(&client->dev, "reg[%02X] := %04X\n", reg, val); |
142 | return 0; |
143 | } |
144 | |
145 | struct rda5807_driver { |
146 | struct v4l2_ctrl_handler ctrl_handler; |
147 | struct video_device video_dev; |
148 | struct i2c_client *i2c_client; |
149 | }; |
150 | |
151 | static const struct v4l2_file_operations rda5807_fops = { |
152 | .owner = THIS_MODULE, |
153 | .unlocked_ioctl = video_ioctl2, |
154 | }; |
155 | |
156 | static int rda5807_update_reg(struct rda5807_driver *radio, |
157 | enum rda5807_reg reg, u16 mask, u16 val) |
158 | { |
159 | int err = 0; |
160 | // TODO: Locking. |
161 | // Or do locking in the caller, in case we ever need to update |
162 | // two registers in one operation? |
163 | err = rda5807_i2c_read(radio->i2c_client, reg); |
164 | if (err >= 0) { |
165 | val |= ((u16)err & ~mask); |
166 | err = rda5807_i2c_write(radio->i2c_client, reg, val); |
167 | } |
168 | return err; |
169 | } |
170 | |
171 | static int rda5807_set_enable(struct rda5807_driver *radio, int enabled) |
172 | { |
173 | u16 val = enabled ? RDA5807_MASK_CTRL_ENABLE : 0; |
174 | int err; |
175 | dev_info(&radio->i2c_client->dev, "set enabled to %d\n", enabled); |
176 | err = rda5807_update_reg(radio, RDA5807_REG_CTRL, |
177 | RDA5807_MASK_CTRL_ENABLE, val); |
178 | if (err < 0) |
179 | return err; |
180 | /* Tuning is lost when the chip is disabled, so re-tune when enabled. */ |
181 | if (enabled) { |
182 | err = rda5807_update_reg(radio, RDA5807_REG_CHAN, |
183 | RDA5807_MASK_CHAN_TUNE, |
184 | RDA5807_MASK_CHAN_TUNE); |
185 | |
186 | /* following the rda5807 programming guide, we |
187 | * need to wait for 0.5 seconds before tune */ |
188 | if (!err) |
189 | msleep(500); |
190 | } |
191 | |
192 | return err; |
193 | } |
194 | |
195 | static int rda5807_set_mute(struct rda5807_driver *radio, int muted) |
196 | { |
197 | u16 val = muted ? 0 : RDA5807_MASK_CTRL_DMUTE /* disable mute */; |
198 | dev_info(&radio->i2c_client->dev, "set mute to %d\n", muted); |
199 | return rda5807_update_reg(radio, RDA5807_REG_CTRL, |
200 | RDA5807_MASK_CTRL_DMUTE, val); |
201 | } |
202 | |
203 | static int rda5807_set_volume(struct rda5807_driver *radio, int volume) |
204 | { |
205 | dev_info(&radio->i2c_client->dev, "set volume to %d\n", volume); |
206 | return rda5807_update_reg(radio, RDA5807_REG_INTM_THRESH_VOL, |
207 | RDA5807_MASK_VOLUME_DAC, |
208 | volume << RDA5807_SHIFT_VOLUME_DAC); |
209 | } |
210 | |
211 | static int rda5807_set_preemphasis(struct rda5807_driver *radio, |
212 | enum v4l2_preemphasis preemp) |
213 | { |
214 | dev_info(&radio->i2c_client->dev, "set preemphasis to %d\n", preemp); |
215 | return rda5807_update_reg(radio, RDA5807_REG_IOCFG, |
216 | RDA5807_MASK_DEEMPHASIS, |
217 | preemp == V4L2_PREEMPHASIS_50_uS |
218 | ? RDA5807_MASK_DEEMPHASIS : 0); |
219 | } |
220 | |
221 | static int rda5807_get_frequency(struct rda5807_driver *radio) |
222 | { |
223 | u32 freq_khz; |
224 | u16 val; |
225 | int err; |
226 | |
227 | err = rda5807_i2c_read(radio->i2c_client, RDA5807_REG_SEEK_RESULT); |
228 | if (err < 0) |
229 | return err; |
230 | val = err; |
231 | |
232 | freq_khz = 50 * (val & RDA5807_MASK_SEEKRES_READCHAN) |
233 | + RDA5807_FREQ_MIN_KHZ; |
234 | dev_info(&radio->i2c_client->dev, "get freq of %u kHz\n", freq_khz); |
235 | return freq_khz; |
236 | } |
237 | |
238 | static int rda5807_set_frequency(struct rda5807_driver *radio, u32 freq_khz) |
239 | { |
240 | u16 mask = 0; |
241 | u16 val = 0; |
242 | |
243 | dev_info(&radio->i2c_client->dev, "set freq to %u kHz\n", freq_khz); |
244 | |
245 | if (freq_khz < RDA5807_FREQ_MIN_KHZ) |
246 | return -ERANGE; |
247 | if (freq_khz > RDA5807_FREQ_MAX_KHZ) |
248 | return -ERANGE; |
249 | |
250 | /* select widest band */ |
251 | mask |= RDA5807_MASK_CHAN_BAND; |
252 | val |= 2 << RDA5807_SHIFT_CHAN_BAND; |
253 | /* select 50 kHz channel spacing */ |
254 | mask |= RDA5807_MASK_CHAN_SPACE; |
255 | val |= 2 << RDA5807_SHIFT_CHAN_SPACE; |
256 | /* select frequency */ |
257 | mask |= RDA5807_MASK_CHAN_WRCHAN; |
258 | val |= ((freq_khz - RDA5807_FREQ_MIN_KHZ + 25) / 50) |
259 | << RDA5807_SHIFT_CHAN_WRCHAN; |
260 | /* start tune operation */ |
261 | mask |= RDA5807_MASK_CHAN_TUNE; |
262 | val |= RDA5807_MASK_CHAN_TUNE; |
263 | |
264 | return rda5807_update_reg(radio, RDA5807_REG_CHAN, mask, val); |
265 | } |
266 | |
267 | static int rda5807_seek_frequency(struct rda5807_driver *radio, |
268 | int upward, int wrap) |
269 | { |
270 | u16 mask = 0; |
271 | u16 val = 0; |
272 | |
273 | /* TODO: Seek threshold is configurable. How should the driver handle |
274 | * this configuration? |
275 | */ |
276 | /* seek up or down? */ |
277 | mask |= RDA5807_MASK_CTRL_SEEKUP; |
278 | if (upward) |
279 | val |= RDA5807_MASK_CTRL_SEEKUP; |
280 | /* wrap around at band limit? */ |
281 | mask |= RDA5807_MASK_CTRL_SKMODE; |
282 | if (!wrap) |
283 | val |= RDA5807_MASK_CTRL_SKMODE; |
284 | /* seek command */ |
285 | mask |= RDA5807_MASK_CTRL_SEEK; |
286 | val |= RDA5807_MASK_CTRL_SEEK; |
287 | |
288 | return rda5807_update_reg(radio, RDA5807_REG_CTRL, mask, val); |
289 | } |
290 | |
291 | static inline struct rda5807_driver *ctrl_to_radio(struct v4l2_ctrl *ctrl) |
292 | { |
293 | return container_of(ctrl->handler, struct rda5807_driver, ctrl_handler); |
294 | } |
295 | |
296 | static int rda5807_s_ctrl(struct v4l2_ctrl *ctrl) |
297 | { |
298 | struct rda5807_driver *radio = ctrl_to_radio(ctrl); |
299 | |
300 | switch (ctrl->id) { |
301 | case V4L2_CID_AUDIO_MUTE: { |
302 | /* Disable the radio while muted, to save power. |
303 | * TODO: We can't seek while the radio is disabled; |
304 | * is that a problem? |
305 | */ |
306 | int err1 = rda5807_set_enable(radio, !ctrl->val); |
307 | int err2 = rda5807_set_mute(radio, ctrl->val); |
308 | return err1 ? err1 : err2; |
309 | } |
310 | case V4L2_CID_AUDIO_VOLUME: |
311 | return rda5807_set_volume(radio, ctrl->val); |
312 | case V4L2_CID_TUNE_PREEMPHASIS: |
313 | return rda5807_set_preemphasis(radio, ctrl->val); |
314 | default: |
315 | return -EINVAL; |
316 | } |
317 | } |
318 | |
319 | static const struct v4l2_ctrl_ops rda5807_ctrl_ops = { |
320 | .s_ctrl = rda5807_s_ctrl, |
321 | }; |
322 | |
323 | static int rda5807_vidioc_querycap(struct file *file, void *fh, |
324 | struct v4l2_capability *cap) |
325 | { |
326 | *cap = (struct v4l2_capability) { |
327 | .driver = "rda5807", |
328 | .card = "RDA5807 FM receiver", |
329 | .bus_info = "I2C", |
330 | .capabilities = V4L2_CAP_RADIO | V4L2_CAP_TUNER |
331 | | V4L2_CAP_HW_FREQ_SEEK, |
332 | }; |
333 | |
334 | return 0; |
335 | } |
336 | |
337 | static int rda5807_vidioc_g_audio(struct file *file, void *fh, |
338 | struct v4l2_audio *a) |
339 | { |
340 | if (a->index != 0) |
341 | return -EINVAL; |
342 | |
343 | *a = (struct v4l2_audio) { |
344 | .name = "Radio", |
345 | .capability = V4L2_AUDCAP_STEREO, |
346 | .mode = 0, |
347 | }; |
348 | |
349 | return 0; |
350 | } |
351 | |
352 | static int rda5807_vidioc_g_tuner(struct file *file, void *fh, |
353 | struct v4l2_tuner *a) |
354 | { |
355 | struct rda5807_driver *radio = video_drvdata(file); |
356 | int err; |
357 | u16 seekres, signal; |
358 | __u32 rxsubchans; |
359 | |
360 | if (a->index != 0) |
361 | return -EINVAL; |
362 | |
363 | err = rda5807_i2c_read(radio->i2c_client, RDA5807_REG_SEEK_RESULT); |
364 | if (err < 0) |
365 | return err; |
366 | seekres = (u16)err; |
367 | if ((seekres & (RDA5807_MASK_SEEKRES_COMPLETE |
368 | | RDA5807_MASK_SEEKRES_FAIL)) |
369 | == RDA5807_MASK_SEEKRES_COMPLETE) |
370 | /* mono/stereo known */ |
371 | rxsubchans = seekres & RDA5807_MASK_SEEKRES_STEREO |
372 | ? V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO; |
373 | else |
374 | /* mono/stereo unknown */ |
375 | rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO; |
376 | |
377 | err = rda5807_i2c_read(radio->i2c_client, RDA5807_REG_SIGNAL); |
378 | if (err < 0) |
379 | return err; |
380 | signal = ((u16)err & RDA5807_MASK_RSSI) >> RDA5807_SHIFT_RSSI; |
381 | |
382 | *a = (struct v4l2_tuner) { |
383 | .name = "FM", |
384 | .type = V4L2_TUNER_RADIO, |
385 | .capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO, |
386 | /* unit is 1/16 kHz */ |
387 | .rangelow = RDA5807_FREQ_MIN_KHZ * 16, |
388 | .rangehigh = RDA5807_FREQ_MAX_KHZ * 16, |
389 | .rxsubchans = rxsubchans, |
390 | /* TODO: Implement forced mono (RDA5807_MASK_CTRL_MONO). */ |
391 | .audmode = V4L2_TUNER_MODE_STEREO, |
392 | .signal = signal << (16 - 7), |
393 | .afc = 0, /* automatic frequency control */ |
394 | }; |
395 | |
396 | return 0; |
397 | } |
398 | |
399 | static int rda5807_vidioc_g_frequency(struct file *file, void *fh, |
400 | struct v4l2_frequency *a) |
401 | { |
402 | struct rda5807_driver *radio = video_drvdata(file); |
403 | int freq_khz; |
404 | |
405 | if (a->tuner != 0) |
406 | return -EINVAL; |
407 | /* This ioctl ignores the type field. */ |
408 | |
409 | freq_khz = rda5807_get_frequency(radio); |
410 | if (freq_khz < 0) |
411 | return freq_khz; |
412 | |
413 | a->frequency = (__u32)freq_khz * 16; |
414 | return 0; |
415 | } |
416 | |
417 | static int rda5807_vidioc_s_frequency(struct file *file, void *fh, |
418 | struct v4l2_frequency *a) |
419 | { |
420 | struct rda5807_driver *radio = video_drvdata(file); |
421 | |
422 | if (a->tuner != 0) |
423 | return -EINVAL; |
424 | if (a->type != V4L2_TUNER_RADIO) |
425 | return -EINVAL; |
426 | |
427 | return rda5807_set_frequency(radio, (a->frequency * 625) / 10000); |
428 | } |
429 | |
430 | static int rda5807_vidioc_s_hw_freq_seek(struct file *file, void *fh, |
431 | struct v4l2_hw_freq_seek *a) |
432 | { |
433 | struct rda5807_driver *radio = video_drvdata(file); |
434 | |
435 | if (a->tuner != 0) |
436 | return -EINVAL; |
437 | if (a->type != V4L2_TUNER_RADIO) |
438 | return -EINVAL; |
439 | |
440 | return rda5807_seek_frequency(radio, a->seek_upward, a->wrap_around); |
441 | } |
442 | |
443 | static const struct v4l2_ioctl_ops rda5807_ioctl_ops = { |
444 | .vidioc_querycap = rda5807_vidioc_querycap, |
445 | .vidioc_g_audio = rda5807_vidioc_g_audio, |
446 | .vidioc_g_tuner = rda5807_vidioc_g_tuner, |
447 | .vidioc_g_frequency = rda5807_vidioc_g_frequency, |
448 | .vidioc_s_frequency = rda5807_vidioc_s_frequency, |
449 | .vidioc_s_hw_freq_seek = rda5807_vidioc_s_hw_freq_seek, |
450 | }; |
451 | |
452 | static int __devinit rda5807_i2c_probe(struct i2c_client *client, |
453 | const struct i2c_device_id *id) |
454 | { |
455 | struct rda5807_platform_data *pdata = client->dev.platform_data; |
456 | struct rda5807_driver *radio; |
457 | int err; |
458 | u16 val; |
459 | |
460 | if (!pdata) { |
461 | dev_err(&client->dev, "Platform data missing\n"); |
462 | return -EINVAL; |
463 | } |
464 | |
465 | err = rda5807_i2c_read(client, RDA5807_REG_CHIPID); |
466 | if (err < 0) { |
467 | dev_err(&client->dev, "Failed to read chip ID (%d)\n", err); |
468 | return err; |
469 | } |
470 | val = err; |
471 | if ((val & 0xFF00) != 0x5800) { |
472 | dev_err(&client->dev, "Chip ID mismatch: " |
473 | "expected 58xx, got %04X\n", val); |
474 | return -ENODEV; |
475 | } |
476 | dev_info(&client->dev, "Found FM radio receiver\n"); |
477 | |
478 | // TODO: Resetting the chip would be good. |
479 | |
480 | radio = devm_kzalloc(&client->dev, sizeof(*radio), GFP_KERNEL); |
481 | if (!radio) { |
482 | dev_err(&client->dev, "Failed to allocate driver data\n"); |
483 | return -ENOMEM; |
484 | } |
485 | |
486 | radio->i2c_client = client; |
487 | |
488 | /* Initialize controls. */ |
489 | v4l2_ctrl_handler_init(&radio->ctrl_handler, 3); |
490 | v4l2_ctrl_new_std(&radio->ctrl_handler, &rda5807_ctrl_ops, |
491 | V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1); |
492 | v4l2_ctrl_new_std(&radio->ctrl_handler, &rda5807_ctrl_ops, |
493 | V4L2_CID_AUDIO_VOLUME, 0, 15, 1, 8); |
494 | /* TODO: V4L2_CID_TUNE_PREEMPHASIS is based on V4L2_CID_FM_TX_CLASS_BASE |
495 | * which suggests it is a transmit control rather than a receive |
496 | * control. The register bit we change is called "de-emphasis", |
497 | * but there is no de-emphasis control in V4L2. |
498 | */ |
499 | v4l2_ctrl_new_std_menu(&radio->ctrl_handler, &rda5807_ctrl_ops, |
500 | V4L2_CID_TUNE_PREEMPHASIS, |
501 | V4L2_PREEMPHASIS_75_uS, |
502 | BIT(V4L2_PREEMPHASIS_DISABLED), |
503 | V4L2_PREEMPHASIS_50_uS); |
504 | err = radio->ctrl_handler.error; |
505 | if (err) { |
506 | dev_err(&client->dev, "Failed to init controls handler (%d)\n", |
507 | err); |
508 | goto err_ctrl_free; |
509 | } |
510 | |
511 | radio->video_dev = (struct video_device) { |
512 | .name = "RDA5807 FM receiver", |
513 | .flags = V4L2_DEBUG_IOCTL | V4L2_DEBUG_IOCTL_ARG, |
514 | .ctrl_handler = &radio->ctrl_handler, |
515 | .fops = &rda5807_fops, |
516 | .ioctl_ops = &rda5807_ioctl_ops, |
517 | .release = video_device_release_empty, |
518 | //.lock = &radio->lock, |
519 | }; |
520 | i2c_set_clientdata(client, radio); |
521 | video_set_drvdata(&radio->video_dev, radio); |
522 | |
523 | err = video_register_device(&radio->video_dev, VFL_TYPE_RADIO, -1); |
524 | if (err < 0) { |
525 | dev_err(&client->dev, "Failed to register video device (%d)\n", |
526 | err); |
527 | goto err_ctrl_free; |
528 | } |
529 | |
530 | /* Configure chip inputs. */ |
531 | err = rda5807_update_reg(radio, RDA5807_REG_INTM_THRESH_VOL, |
532 | 0xF << 4, (pdata->input_flags & 0xF) << 4); |
533 | if (err < 0) { |
534 | dev_warn(&client->dev, "Failed to configure inputs (%d)\n", |
535 | err); |
536 | } |
537 | /* Configure chip outputs. */ |
538 | val = 0; |
539 | if (pdata->output_flags & RDA5807_OUTPUT_AUDIO_I2S) { |
540 | val |= BIT(6); |
541 | } else if (pdata->output_flags & RDA5807_OUTPUT_STEREO_INDICATOR) { |
542 | val |= BIT(4); |
543 | } |
544 | err = rda5807_update_reg(radio, RDA5807_REG_IOCFG, 0x003F, val); |
545 | if (err < 0) { |
546 | dev_warn(&client->dev, "Failed to configure outputs (%d)\n", |
547 | err); |
548 | } |
549 | val = 0; |
550 | if (pdata->output_flags & RDA5807_OUTPUT_AUDIO_ANALOG) { |
551 | val |= BIT(15); |
552 | } |
553 | err = rda5807_update_reg(radio, RDA5807_REG_CTRL, BIT(15), val); |
554 | if (err < 0) { |
555 | dev_warn(&client->dev, "Failed to configure outputs (%d)\n", |
556 | err); |
557 | } |
558 | |
559 | err = v4l2_ctrl_handler_setup(&radio->ctrl_handler); |
560 | if (err < 0) { |
561 | dev_err(&client->dev, "Failed to set default control values" |
562 | " (%d)\n", err); |
563 | goto err_video_unreg; |
564 | } |
565 | |
566 | return 0; |
567 | |
568 | err_video_unreg: |
569 | video_unregister_device(&radio->video_dev); |
570 | |
571 | err_ctrl_free: |
572 | v4l2_ctrl_handler_free(&radio->ctrl_handler); |
573 | |
574 | /*err_radio_rel:*/ |
575 | video_device_release_empty(&radio->video_dev); |
576 | |
577 | return err; |
578 | } |
579 | |
580 | static int __devexit rda5807_i2c_remove(struct i2c_client *client) |
581 | { |
582 | struct rda5807_driver *radio = i2c_get_clientdata(client); |
583 | |
584 | video_unregister_device(&radio->video_dev); |
585 | v4l2_ctrl_handler_free(&radio->ctrl_handler); |
586 | video_device_release_empty(&radio->video_dev); |
587 | |
588 | return 0; |
589 | } |
590 | |
591 | #ifdef CONFIG_PM |
592 | |
593 | static int rda5807_suspend(struct device *dev) |
594 | { |
595 | struct i2c_client *client = to_i2c_client(dev); |
596 | struct rda5807_driver *radio = i2c_get_clientdata(client); |
597 | |
598 | return rda5807_set_enable(radio, 0); |
599 | } |
600 | |
601 | static int rda5807_resume(struct device *dev) |
602 | { |
603 | struct i2c_client *client = to_i2c_client(dev); |
604 | struct rda5807_driver *radio = i2c_get_clientdata(client); |
605 | struct v4l2_ctrl *mute_ctrl = v4l2_ctrl_find(&radio->ctrl_handler, |
606 | V4L2_CID_AUDIO_MUTE); |
607 | s32 mute_val = v4l2_ctrl_g_ctrl(mute_ctrl); |
608 | int enabled = !mute_val; |
609 | |
610 | if (enabled) |
611 | return rda5807_set_enable(radio, enabled); |
612 | else |
613 | return 0; |
614 | } |
615 | |
616 | static SIMPLE_DEV_PM_OPS(rda5807_pm_ops, rda5807_suspend, rda5807_resume); |
617 | #define RDA5807_PM_OPS (&rda5807_pm_ops) |
618 | |
619 | #else |
620 | |
621 | #define RDA5807_PM_OPS NULL |
622 | |
623 | #endif |
624 | |
625 | static const struct i2c_device_id rda5807_id[] = { |
626 | { "radio-rda5807", 0 }, |
627 | { } |
628 | }; |
629 | MODULE_DEVICE_TABLE(i2c, rda5807_id); |
630 | |
631 | static struct i2c_driver rda5807_i2c_driver = { |
632 | .probe = rda5807_i2c_probe, |
633 | .remove = __devexit_p(rda5807_i2c_remove), |
634 | .id_table = rda5807_id, |
635 | .driver = { |
636 | .name = "radio-rda5807", |
637 | .owner = THIS_MODULE, |
638 | .pm = RDA5807_PM_OPS, |
639 | }, |
640 | }; |
641 | |
642 | module_i2c_driver(rda5807_i2c_driver); |
643 | |
644 | MODULE_AUTHOR("Maarten ter Huurne <maarten@treewalker.org>"); |
645 | MODULE_DESCRIPTION("RDA5807 FM tuner driver"); |
646 | MODULE_LICENSE("GPL"); |
647 |
Branches:
ben-wpan
ben-wpan-stefan
javiroman/ks7010
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9